# Arduino 教程

## Arduino软件安装，安装Pico开发板及上传Pico固件

1.安装Windows驱动程序

![](media/6cf6312dc7c7db27794b54d58a8bf80c.png)

### 1.1.下载安装arduino软件： 

（1）首先，进入arduino官方网站：[<u>https://www.arduino.cc/</u>](https://www.arduino.cc/)
，点击“SOFTWARE”进入下载页面，如下图所示：

![](media/bfe8c9e405c71123dee7921eddff86d3.png)

![](media/7250961db41ba42e4b881d77bd76a319.png)

（2）然后，根据你的操作系统选择并下载相应的安装程序。如果你是Windows用户，请选择“Windows安装程序”下载正确的安装驱动程序。

![](media/894116c5cf0023dd9720946cfb441790.png)

选择点击Windows Win7 and newer下载Arduino 1.8.16版本的安装程序，需要手动安装。而当点击Windows ZIP file时，Arduino 1.8.16版本的zip文件将被直接下载，只需要解压缩它就可以完成安装。

![](media/a983a2f2eceb968afbff8ba0f0376240.png)

一般情况下，点击JUST DOWNLOAD就可以下载了，当然如果你愿意，你可以选择小小的赞助，以帮助伟大的Arduino开源事业。

（3）Arduino软件下载完成后，继续安装，当你收到操作系统的警告时，请允许驱动程序安装。首先点击I Agree, 然后选择好要安装的组件后再点击Next。

![](media/00e334d3c756a2495da6f0d1b2db680a.png)

![](media/de541d90a1cda992ad8e3f0cbaf95f94.png)

（4）选择安装目录(我们建议保持默认目录)，然后点击Install。

![](media/7da9aca1e8432c59372e7c7ab2574bd9.png)

（5）如果出现以下界面，则应选择Install。

![](media/85b29de2aa791ecc77280ccde91e53c5.png)

该过程将提取并安装所有必需的文件，以正确执行Arduino软件(IDE)。

![](media/739c41701fbcab202f0e587f534bad30.png)

安装完成后，会在桌面上生成一个Arduino 软件快捷方式。

![](media/d28223c55a30f949760779720fe4ec24.png)

在电脑桌面上点击Arduino 软件快捷方式打开Arduino IDE.

![](media/47de9686901274baacefd7638a8469dd.png)

点击“File”→“Preferences”，选择“简体中文”之后点击“好”。这样就对语言进行设置好了。

![](media/205b49ebf9235c16dcb3e9e69accc242.png)

关掉Arduino IDE，重新点击Arduino 软件快捷方式打开Arduino IDE。这样，英文页面就转化成中文页面。

![](media/e337a3f2d4c15e704621bdf9f01b80a0.png)

![](media/3ebf70bd0365899bd8b7045e0d283de7.png)

A - 用于检查是否存在任何编译错误。

B - 用于将程序上传到Arduino控制板。

C - 用于创建新草图的快捷方式。

D - 用于直接打开示例草图之一。

E - 用于保存草图。

F - 用于从板接收串行数据并将串行数据发送到板的串行监视器。

### 1.2.安装开发板Pico 

（1）网络连接良好的情况下，打开Arduino IDE，单击“工具”→“开发板”→“开发板管理器...”。

![](media/9a37ae7abb7e9a20c5607e91bb39ff9f.png)

2.  在搜索框中输入Pico，选择Arduino Mbed OS RP2040     Boards，点击安装或更新。

    ![](media/cb91d24d99f6920bac88eaf0cec2018c.png)

    （3）安装过程中，当你收到操作系统的安全警告时，请单击安装允许设备软件安装。

    ![](media/f9dbaba2b93d25b034f8104441e47ebb.png)

（4）安装完成后，单击关闭就可以了。

![](media/69df4986bf81ad50b94ba1c5d84bddf4.png)

### 1.3.上传Arduino兼容的Pico固件 

如果你的树莓派Pico板是新的，想使用Arduino学习和开发，则需要上传一个Adruino兼容的Pico固件。请参考以下步骤配置:

（1）断开树莓派Pico板与电脑的连接。继续按树莓派Pico板上的白色按钮(BOOTSEL)，并在松开按钮前将树莓派Pico板连接到电脑。(注意：MicroUSB线连接到树莓派Pico板前一定要一直按住按钮，否则固件下载不会成功)

![](media/33c91d51b2aeb2c943691706354aaad1.png)

（2）打开Arduino IDE，单击 文件→示例→01.Basics→Blink。

![](media/16c21ea3c5050565b7f4c29877e977e3.png)

（3）单击工具→开发板:→Arduino Mbed OS RP2040 Boards→Raspberry Pi Pico。

![](media/33ecc22938ff1ed553db9cac6efd4308.png)

（4）上传草图（Blink）到 Raspberry Pi Pico。

![](media/6a7cc34aca1dce80062722791f444ff1.png)

当草图完成上传时，可以看到以下提示。

![](media/5497cc6362c67b70c57d02fd64566aa6.png)

树莓派Pico板上的指示灯开始闪烁。

![](media/e12ecf9006915a143134d18d53643ee4.png)

单击工具→端口→COMx(Raspberry Pi Pico)。COMx的X在不同的电脑上是不同的。请在你的电脑上选择正确的COM口。在这个的例子中，它是COM15。

![](media/00202833079b6be3b59cc7fac3cda48e.png)

注意:

1.  第一次使用Arduino上传Raspberry Pi     Pico的草图时，不需要选择端口。之后，每次上传草图前，请检查端口是否已选择;否则可能导致代码上传失败。

2.  有时在使用时，Raspberry Pi     Pico可能会由于代码丢失固件而无法工作。此时，你可以如上所述步骤上传Raspberry     Pi Pico的固件。

2.  安装MAC驱动程序

![](media/a6fc83596009c574d8e29ef383748549.png)

### 2.1.下载安装Arduino IDE: 

![](media/5d58d3cf67b308423ddb9f286f6cb697.png)

接下来的操作类似于Windows系统，可以参考上面的Windows系统操作过程。

## 项目教程

### 项目 01: Hello World

项目介绍：

对于树莓派Pico初学者，我们将从一些简单的东西开始。在这个项目中，您只需要一个树莓派Pico板和USB线来完成“Hello World!”项目。它不仅是树莓派Pico板和PC的通信测试，也是树莓派Pico板的初级项目。

项目元件：

|  ![](media/8ea81d60b8e2132c358041235490b7d5.jpeg)  |  ![](media/3bdcc62cfa661d2b860a76e28537e21e.png)  |
|-|-|
| 树莓派Pico板*1 | USB 线*1 |

项目接线：

在本项目中，我们通过USB线将树莓派Pico板和电脑连接起来。

![Img](./media/img-20251210143823.png)

项目代码：

```C
/*
 * 文件名 : Hello World
 * 描述 : 输入字母R，串口显示“Hello World”.
 * 作者 : www.keyes-robot.com
*/
char val;// 定义变量“val”
void setup()
{
Serial.begin(115200);// 设置波特率为115200
}
void loop()
{
  if (Serial.available() > 0) {
    val=Serial.read();// 读取并赋值给变量val
    if(val=='R')// 检查输入的字母“R”
    {  // 如果是这样的话,    
     Serial.println("Hello World!");// 显示“Hello World !”.
    }
  }
}
```
在上传项目代码到树莓派Pico板之前，请检查Arduino IDE的配置。

单击“工具”，确认板型和端口如下所示：

![](media/4984d5958eb182fabaa9927a95e77b24.png)

单击![](media/b0d41283bf5ae66d2d5ab45db15331ba.png)将项目代码上传到树莓派Pico板。

![](media/7553cbc6e5cf1a09daa27a89a801265e.png)

项目代码上传成功！

![](media/b242b0f515977611a11a0dbf7c2b45b9.png)

项目结果：

项目代码上传成功后，单击![](media/2f6bca56f724e45a855335cb53ae9b4e.png)图标进入串行监视器，设置波特率为115200，在文本框输入字母“R”，单击“Send”，这样串口监视器打印“Hello World!”。

![](media/76e306da07eb3054ee8878da268ceb7b.png)

### 项目 02：板载灯闪烁

项目介绍：

树莓派Pico板上有个板载LED，这个LED是固定接在树莓派Pico板上的GP25引脚，在这个项目中，我们将来学习使板载LED闪烁。

项目元件：

|  ![](media/8ea81d60b8e2132c358041235490b7d5.jpeg)  |  ![](media/3bdcc62cfa661d2b860a76e28537e21e.png)  |
|-|-|
| 树莓派Pico板*1 | USB 线*1 |

项目接线：

在本项目中，我们通过USB线将树莓派Pico板和电脑连接起来

![Img](./media/img-20251210143823.png)

项目代码：

树莓派Pico板上的板载LED是由GP25控制，当GP25输出高电平时，LED点亮;当输出低时，LED灯关闭。

```C
/*
 * 文件名 : 板载灯闪烁
 * 描述 : 使led闪烁.
 * 作者 : www.keyes-robot.com
*/
#define LED_BUILTIN 25

// 复位或单板上电时，设置功能运行一次
void setup() {
  // 初始化数字引脚LED_BUILTIN作为输出.
  pinMode(LED_BUILTIN, OUTPUT);
}

// 循环函数会一直循环下去
void loop() {
  digitalWrite(LED_BUILTIN, HIGH);   // 打开LED (HIGH为高电平)
  delay(1000);                       // 延时1秒
  digitalWrite(LED_BUILTIN, LOW);    // 关闭LED(LOW为低电平)
  delay(1000);                       // 延时1秒
}
```
在上传项目代码到树莓派Pico板之前，请检查Arduino IDE的配置。

单击“工具”，确认板型和端口如下所示：

![](media/c85f2c2f16853a3b05edbe7b397e7cd5.png)

单击![](media/b0d41283bf5ae66d2d5ab45db15331ba.png)将项目代码上传到树莓派Pico板。

![](media/f8e0b27ed60d19ef44857a62644220c6.png)

项目代码上传成功！

![](media/51caf4d91a0ba29cb9c5f78575db7f1b.png)

项目结果：

项目代码上传成功后，利用USB线上电，可以看到的现象是：树莓派Pico板的LED开始闪烁，循环进行。

![](media/e12ecf9006915a143134d18d53643ee4.png)

### 项目 03：外接灯闪烁 

项目介绍：

在这个项目中，我们将向你展示外接LED闪烁效果。我们使用树莓派Pico板的数字引脚打开LED，让它闪烁。

项目元件：

|  ![](media/6046d0c7d3a11a080a2de23b1969804e.jpeg)  |  ![](media/2762753d227ba94de9f6e5c9ff79fe53.png)  |  ![](media/3ac518b4caa5086041545c60c7a6a2d1.png)  |  ![](media/7dcbd02995be3c142b2f97df7f7c03ce.png)  |
|-|-|-|-|
| 树莓派Pico板*1 | 树莓派Pico板的扩展板*1 | 公对母杜邦线若干 | USB 线*1 |
|  ![](media/7eb361d680dfa351f07f8527aeb37abd.png)  |  ![](media/098a2730d0b0a2a4b2079e0fc87fd38b.png)  |  ![](media/e380dd26e4825be9a768973802a55fe6.png)  |  |
| 红色LED*1 | 220Ω电阻*1 | 面包板*1 |  |

元件知识：

（1）LED:

![LED](./media/led.jpg)

LED是一种被称为“发光二极管”的半导体，是一种由半导体材料(硅、硒、锗等)制成的电子器件。它有正极和负极。短腿为负极，接GND，长腿为正极，接3.3V或5V。

![](media/14a84d5f016d7566151a5563c502787e.png)

（2）五色环电阻

电阻是电路中限制或调节电流流动的电子元件。左边是电阻器的外观，右边是电阻在电路中表示的符号。电阻(R)的单位为欧姆(Ω)，1 mΩ= 1000 kΩ，1kΩ= 1000Ω。

![](media/8a86f65cf820d08e8956daa70d1c4195.jpeg)
![](media/f6079fe22518f0fc1b0c3a3b93a516a1.png)

我们可以使用电阻来保护敏感组件，如LED。电阻的强度（以Ω为单位）用小色环标记在电阻器的主体上。每种颜色代表一个数字，你可以用电阻对照卡查找。

\-色带1 – 1st Digit.

-色带 2 – 2nd Digit.

-色带 3 – 3rd Digit.

-色带 4 – Multiplier.

-色带 5 – Tolerance.

![](media/c3df005312cd9f6d4cdae6abf3cddb83.png)

在这个套件中，我们提供了3个具有不同电阻值的五色环电阻。这里以3个五色环电阻为例：

220Ω 电阻\*10

![](media/793740d0b936e516ca354111e2d0eb79.png)

10KΩ 电阻\*10

![](media/18484e5d16b6d89c63825cc2efa6a543.png)

1KΩ 电阻\*10

![](media/8088ed382616afb346d44f5aacfb52d1.png)

在相同的电压下，会有更小的电流和更大的电阻。电流(I)、电压(U)、电阻(R)之间的联系可以用公式表示：I=U/R。在下图中，假如电压为3V，则通过R1的电流:I = U / R = 3 V / 10 KΩ= 0.0003A= 0.3mA。

![](media/b3eec552e4dfad361833730698621776.png)

不要把电阻值很低的电阻直接连接在电源两极，这样会使电流过高而损坏电子元件。电阻是没有正负极之分。

（3）面包板

面包板是实验室中用于搭接电路的重要工具。面包板上有许多孔，可以插入集成电路和电阻等电路元件。熟练掌握面包板的使用方法是提高实验效率，减少实验故障出现几率的重要基础之一。下面就面包板的结构和使用方法做简单介绍。一个典型的面包板如下所示：

![](media/837cd6ec4b1b09cc46340201a6425958.jpeg)

面包板的外观和内部结构如上图所示，常见的最小单元面包板分上、中、下三部分，上面和下面部分一般是由一行或两行的插孔构成的窄条，中间部分是由中间一条隔离凹槽和上下各5
行的插孔构成的条。

![](media/099510035abc223273495e042a7bd6b6.jpeg)

在面包板的两个窄条分别有两行插孔，两行之间是不连通的，一般是作为电源引入的通路。上方第一行标有“+”的一行有10组插孔（内部都是连通），均为正极；上方第二行标有“-”的一行有10组插孔，（内部都是连通），均为接地。面包板下方的第一行与第二行结构同上。如需用到整个面包板，通常将“+”与“+”用导线连接起来，“-”与“-”用导线连接起来。

　　中间部分宽条是由中间一条隔离凹槽和上下各5
行的插孔构成。在同一列中的5
个插孔是互相连通的，列和列之间以及凹槽上下部分则是不连通的。外观及结构如下图：

![](media/3fc9a04d9354e63ca0e89eb7ed627128.png)

中间部分宽条的连接孔分为上下两部分，是面包板的主工作区，用来插接原件和跳线。在同一列中的5个插孔（即a-b-c-d-e，f-g-h-i-j）是互相连通的；列和列之间以及凹槽上下部分是不连通的。在做实验的时候，通常是使用两窄一宽组成的小单元，在宽条部分搭接电路的主体部分，上面的窄条取一行做电源，下面的窄条取一行做接地。中间宽条用于连接电路，由于凹槽上下是不连通的，所以集成块一般跨插在凹槽上。

（4）keyestudio 树莓派Pico板的扩展板使用方法

将树莓派Pico板堆叠在扩展板上即可使用，如下图：

![](media/027bcb15b34415d54164c03a796a10ab.jpeg)

（5）电源

在本项目中，我们用USB线将Raspberry Pi Pico和电脑连起来。连接方法如下：

将数据线一端先接到电脑。再按住树莓派 Pico 板上的白色按钮(BOOTSEL)，最后将数据线的另外一端接到树莓派 Pico 板，确认接好后再松开白色按钮(BOOTSEL)。

![26](./media/26.png)

![27](./media/27.gif)

项目电路图和接线图：

首先，切断树莓派Pico板的所有电源。然后根据电路图和接线图搭建电路。电路搭建好并验证无误后，用USB线将树莓派Pico板连接到电脑上。注意：避免任何可能的短路(特别是连接3.3V和GND)!

警告：短路可能导致电路中产生大电流，造成元件过热，并对硬件造成永久性损坏。

![](media/cb069d7553d861e3293d8bdbe85bbd05.png)



![](media/96800765a20d72653a8cd4a9ae12b636.png)



注意:

怎样连接LED

![](media/14a84d5f016d7566151a5563c502787e.png)

怎样识别五色环220Ω电阻

![](media/793740d0b936e516ca354111e2d0eb79.png)

项目代码：

根据电路图，当树莓派Pico板的GP16输出高电平时，LED点亮；当输出低电平时，LED灯关闭。因此，我们可以通过控制GP16重复输出高低电平，使LED反复闪烁。

```C
/*
 * 文件名 : 外接灯闪烁
 * 描述 : 使led闪烁.
 * 作者 : www.keyes-robot.com
*/
#define PIN_LED   16   //定义led引脚

// 复位或单板上电时，设置功能运行一次
void setup() {
  // 初始化数字引脚LED作为输出.
  pinMode(PIN_LED, OUTPUT);
}

// 循环函数会一直循环下去
void loop() {
  digitalWrite(PIN_LED, HIGH);   // 打开LED (HIGH为高电平)
  delay(500);                       // 延时0.5秒
  digitalWrite(PIN_LED, LOW);    // 关闭LED (LOW为低电平)
  delay(500);                       // 延时0.5秒
}

```

在上传项目代码到树莓派Pico板之前，请检查Arduino IDE的配置。

单击“工具”，确认板型和端口如下所示：

![](media/00cb510e12256d8cbbf96ffeb133d297.png)

单击![](media/b0d41283bf5ae66d2d5ab45db15331ba.png)将项目代码上传到树莓派Pico板。

![](media/35ab4cd24eb4f4d961355e5fc5622421.png)

项目代码上传成功！

![](media/918c8a394cbaab8879a4ab0954442611.png)

项目结果：

项目代码上传成功后，利用USB线上电，可以看到的现象是：电路中的LED以0.5S的频率开始闪烁，循环进行。

![](media/77dec960e108229b6d97b4af9a2db902.png)


### 项目 04：呼吸灯

项目介绍：

在之前的研究中，我们知道LED有亮/灭状态，那么如何进入中间状态呢?如何输出一个中间状态让LED“半亮”?这就是我们将要学习的。呼吸灯，即LED由灭到亮，再由亮到灭，就像“呼吸”一样。那么，如何控制LED的亮度呢?我们将使用树莓派Pico板的PWM来实现这个目标。

项目元件：

|  ![](media/6046d0c7d3a11a080a2de23b1969804e.jpeg)  |  ![](media/2762753d227ba94de9f6e5c9ff79fe53.png)  |  ![](media/3ac518b4caa5086041545c60c7a6a2d1.png)  |  ![](media/7dcbd02995be3c142b2f97df7f7c03ce.png)  |
|-|-|-|-|
| 树莓派Pico板*1 | 树莓派Pico板的扩展板*1 | 公对母杜邦线若干 | USB 线*1 |
|  ![](media/7eb361d680dfa351f07f8527aeb37abd.png)  |  ![](media/098a2730d0b0a2a4b2079e0fc87fd38b.png)  |  ![](media/e380dd26e4825be9a768973802a55fe6.png)  |  |
| 红色LED*1 | 220Ω电阻*1 | 面包板*1 |  |

元件知识：

![](media/6549bdbfd4e7b6b2b341012105d655e8.png)

Analog & Digital

模拟信号在时间和数值上都是连续的信号。相反，数字信号或离散时间信号是由一系列数字组成的时间序列。生活中的大多数信号都是模拟信号。一个熟悉的模拟信号的例子是，全天的温度是如何不断变化的，而不是突然从0到10的瞬间变化。然而，数字信号的值可以瞬间改变，这个变化用数字表示为1和0(二进制代码的基础)。如下图所示，我们可以更容易地看出它们的差异。

![](media/4bdf6127e563b453a1fd8953b4ebb277.png)

在实际应用中，我们经常使用二进制作为数字信号，即一系列的0和1。由于二进制信号只有两个值(0或1)，因此具有很大的稳定性和可靠性。最后，可以将模拟信号和数字信号相互转换。

PWM：

脉宽调制(PWM)是一种利用数字信号控制模拟电路的有效方法。普通处理器不能直接输出模拟信号。PWM技术使这种转换(将数字信号转换为模拟信号)非常方便。PWM技术利用数字引脚发送一定频率的方波，即高电平和低电平的输出，交替持续一段时间。每一组高电平和低电平的总时间一般是固定的，称为周期(注:周期的倒数是频率)。高电平输出的时间通常称为脉宽，占空比是脉宽(PW)与波形总周期(T)之比的百分比。高电平输出持续时间越长，占空比越长，模拟信号中相应的电压也就越高。下图显示了对应于脉冲宽度0%-100的模拟信号电压在0V-3.3V(高电平为3.3V)之间的变化情况.

![](media/a439e1bd8a4578b43b7188c821d58594.jpeg)

PWM占空比越长，输出功率越高。既然我们了解了这种关系，我们就可以用PWM来控制LED的亮度或直流电机的速度等等。从上面可以看出，PWM并不是真实的模拟信号，电压的有效值等于相应的模拟信号。因此，我们可以控制LED和其他输出模块的输出功率，以达到不同的效果

树莓派Pico板与 PWM

树莓派Pico板有16个PWM通道，每个PWM通道可以独立控制频率和占空比，时钟频率范围为7Hz到125MHz。树莓派Pico板上的每个引脚都可以配置为PWM输出。

项目电路图和接线图：

![](media/cb069d7553d861e3293d8bdbe85bbd05.png)

![](media/96800765a20d72653a8cd4a9ae12b636.png)

项目代码：

本项目设计使GP16输出PWM，脉宽由0%逐渐增加到100%，再由100%逐渐减小到0%。

```C
/*
 * 文件名 : 呼吸灯
 * 描述 : 让led灯像呼吸一样忽隐忽现.
 * 作者 : www.keyes-robot.com
*/
#define PIN_LED   16   //定义led引脚

void setup() {
  pinMode(PIN_LED, OUTPUT);
}

void loop() {
  for (int i = 0; i < 255; i++) { //使LED逐渐点亮
    analogWrite(PIN_LED, i);
    delay(5);
  }
  for (int i = 255; i > -1; i--) {  //使LED逐渐熄灭
    analogWrite(PIN_LED, i);
    delay(5);
  }
}
```

在上传项目代码到树莓派Pico板之前，请检查Arduino IDE的配置。

单击“工具”，确认板型和端口如下所示：

![](media/b110280fc60554d048620586f785186e.png)

单击![](media/b0d41283bf5ae66d2d5ab45db15331ba.png)将项目代码上传到树莓派Pico板。

![](media/8fccaff28e5cc2d55449fc34fbc25fec.png)

项目代码上传成功！

![](media/33844df20470b80212151b967885a586.png)

项目现象：

项目代码上传成功后，利用USB线上电，可以看到的现象是：电路中的LED从暗逐渐变亮，再从亮逐渐变暗，就像呼吸一样，循环进行。

![](media/e4789bf063f7fdca158b7a87011a02de.png)
![](media/3673c95868f245ee28365de8e51d2ced.png)

### 项目 05：交通灯

项目介绍：

交通灯在我们的日常生活中很普遍。根据一定的时间规律，交通灯是由红、黄、绿三种颜色组成的。每个人都应该遵守交通规则，这可以避免许多交通事故。在这个项目中，我们将使用树莓派Pico板和一些led(红，黄，绿)来模拟交通灯。

项目元件：

|  ![](media/e9de1e032b926d52712bfc484e6cdfe7.jpeg)  |  ![](media/fccf6ad22d4013279f27b3923d0b87fd.png)  |  ![](media/3ac518b4caa5086041545c60c7a6a2d1.png)  |  ![](media/afa6edd3ff90b027a6f43995a6fb15a2.png)  |  ![](media/0c1b0f91b4e56bcbc235d06b48809ac9.png)  |
|-|-|-|-|-|
| 树莓派Pico板*1 | 树莓派Pico板的扩展板*1 | 公对母杜邦线若干 | 红色LED*1 | 黄色 LED*1 |
|  ![](media/6c688493b558ed5f3e90e7dab38cbd93.png)  |  ![](media/7dcbd02995be3c142b2f97df7f7c03ce.png)  |  ![](media/098a2730d0b0a2a4b2079e0fc87fd38b.png)  |  ![](media/b57b4057770f0bcc43f037c0ab8e1c41.png)  |  ![](media/c801a7baee258ff7f5f28ac6e9a7097b.png)  |
| 绿色LED*1 | USB 线*1 | 220Ω电阻*3 | 面包板*1 | 跳线若干 |

项目电路图和接线图：

![](media/92bcb9557428316b25d5273e25665210.png)![](media/41ae8ac64c0c776dd22e6518240c56a1.png)

项目代码：

```C
/*
 * 文件名 : 交通灯
 * 描述 : 模拟交通灯.
 * 作者 : www.keyes-robot.com
*/
#define PIN_LED_RED   16   //定义红色led引脚
#define PIN_LED_YELLOW   17   //定义黄色led引脚
#define PIN_LED_GREEN  18   //定义绿色led引脚

void setup() {
  pinMode(PIN_LED_RED, OUTPUT);
  pinMode(PIN_LED_YELLOW, OUTPUT);
  pinMode(PIN_LED_GREEN, OUTPUT);
}

void loop() {
  digitalWrite(PIN_LED_GREEN, HIGH);// 点亮绿色led
  delay(5000);// 延时5秒
  digitalWrite(PIN_LED_GREEN, LOW); // 关闭绿色led
  for(int i=0;i<3;i++)// 闪烁3次.
  {
    delay(500);// 延时0.5秒
    digitalWrite(PIN_LED_YELLOW, HIGH);// 点亮黄色led灯
    delay(500);// 延时0.5秒
    digitalWrite(PIN_LED_YELLOW, LOW);// 关闭黄色led灯
  } 
  delay(500);// 延时0.5秒
  digitalWrite(PIN_LED_RED, HIGH);// 点亮红色led灯
  delay(5000);// 延时 5 秒
  digitalWrite(PIN_LED_RED, LOW);// 关闭红色led灯
  }
```

项目现象：

项目代码上传成功后，利用USB线上电，可以看到的现象是：1.首先，绿灯会亮5秒，然后熄灭；2.其次，黄灯会闪烁3次，然后熄灭；3.然后，红灯会亮5秒，然后熄灭；4.继续运行上述1-3个步骤。

### 项目 06：RGB

项目介绍：

![](media/94bdff69e438989d8e0934e57f2e5c00.png)

RGB led由三种颜色(红、绿、蓝)组成，通过混合这三种基本颜色可以发出不同的颜色。在这个项目中，我们将向你介绍RGB LED，并向你展示如何使用树莓派Pico板控制RGB LED发出不同的颜色光。即使RGB LED是非常基本的，但这也是一个介绍自己或他人电子和编码基础的伟大方式。

项目元件：

|  ![](media/b18fe281156b29c44796f72222718d58.jpeg)  |  ![](media/6092b6a258930909aa3e098283b36c87.png)  |  ![](media/3ac518b4caa5086041545c60c7a6a2d1.png)  |  ![](media/f1a86fc81ab4b043263ce7e01e14d470.png)  |
|-|-|-|-|
| 树莓派Pico板*1 | 树莓派Pico板的扩展板*1 | 公对母杜邦线若干 | RGB LED*1 |
|  ![](media/098a2730d0b0a2a4b2079e0fc87fd38b.png)  |  ![](media/e380dd26e4825be9a768973802a55fe6.png)  |  ![](media/7dcbd02995be3c142b2f97df7f7c03ce.png)  |  |
| 220Ω电阻*3 | 面包板*1 | USB 线*1 |  |

元件知识：

显示器大多遵循RGB颜色标准，电脑屏幕上的所有颜色都是由红、绿、蓝三种颜色以不同比例混合而成。

![](media/32abd117bdfbba2f79a0e156048b9d22.png)![](media/5a0792145e8a7d9038bf9de389d75fc6.png)

这个RGB LED有4个引脚，每个颜色(红，绿，蓝)和一个共同的阴极。为了改变RGB led的亮度，我们可以使用树莓派Pico板的PWM引脚。PWM引脚会给RGB led不同占空比的信号以获得不同的颜色。

如果我们使用3个10位PWM来控制RGBLED，理论上我们可以通过不同的组合创建2<sup>10</sup>\*2<sup>10</sup>\*2<sup>10</sup>=
1,073,741,824(10亿)种颜色。

项目电路图和接线图：

![](media/f6950bc8498e6139cbb67db84cdd5a9a.png)

![](media/c18f869a9b18fc521c3d09be942efebd.png)

项目代码：

我们需要创建三个PWM通道，并使用随机占空比来使RGB LED亮随机颜色灯。

```C
/*
 * 文件名 : RGB
 * 描述 : 使用RGBLED显示随机颜色.
 * 作者 : www.keyes-robot.com
*/
int ledPins[] = {18, 17, 16};    //定义红，绿，蓝led引脚
int red, green, blue;
void setup() {
  for (int i = 0; i < 3; i++) {   //设置pwm通道，1KHz,8bit
    pinMode(ledPins[i], OUTPUT);
  }
}

void loop() {
  red = random(0, 255);
  green = random(0, 255);
  blue = random(0, 255);
  setColor(red, green, blue);
  delay(1000);
}

void setColor(byte r, byte g, byte b) {
  analogWrite(ledPins[0], 255-r); //共阴极LED，高电平点亮LED.
  analogWrite(ledPins[1], 255-g);
  analogWrite(ledPins[2], 255-b);
}
```

项目现象：

项目代码上传成功后，利用USB线上电，可以看到的现象是：RGB     LED开始显示随机颜色。

### 项目 07：流水灯

项目介绍：

在日常生活中，我们可以看到许多由不同颜色的led组成的广告牌。他们不断地改变灯光(像流水一样)来吸引顾客的注意。在这个项目中，我们将使用树莓派Pico板控制10个leds实现流水的效果。

项目元件：

|  ![](media/b18fe281156b29c44796f72222718d58.jpeg)  |  ![](media/68aa11662f0455d6b5f1965a29336313.png)  |  ![](media/3ac518b4caa5086041545c60c7a6a2d1.png)  |  ![](media/3ec5906fad2172708d449390140f55e6.png)  |
|-|-|-|-|
| 树莓派Pico板*1 | 树莓派Pico板的扩展板*1 | 公对母杜邦线若干 | 红色LED*10 |
|  ![](media/098a2730d0b0a2a4b2079e0fc87fd38b.png)  |  ![](media/e380dd26e4825be9a768973802a55fe6.png)  |  ![](media/c801a7baee258ff7f5f28ac6e9a7097b.png)  |  ![](media/7dcbd02995be3c142b2f97df7f7c03ce.png)  |
| 220Ω电阻*10 | 面包板*1 | 跳线若干 | USB 线*1 |

项目电路图和接线图:

![](media/e6f92039d131685369db2d1ac2c30267.png)

![](media/e3d6a1880f61fdfaec74360190f5ed43.png)

项目代码：

```C
/* 
 * 文件名  : 流水灯
 * 描述 : 用10个led来展示流动的灯.
 * 作者 : www.keyes-robot.com
*/
byte ledPins[] = {16, 17, 18, 19, 20, 21, 22, 26, 27, 28};
int ledCounts;

void setup() {
  ledCounts = sizeof(ledPins);
  for (int i = 0; i < ledCounts; i++) {
    pinMode(ledPins[i], OUTPUT);
  }
}

void loop() {
  for (int i = 0; i < ledCounts; i++) {
    digitalWrite(ledPins[i], HIGH);
    delay(100);
    digitalWrite(ledPins[i], LOW);
  }
  for (int i = ledCounts - 1; i > -1; i--) {
    digitalWrite(ledPins[i], HIGH);
    delay(100);
    digitalWrite(ledPins[i], LOW);
  }
}
```

本项目是设计制作一个流水灯。这是这些行动：首先打开LED，然后关闭它。然后打开LED，然后关闭…并对所有10个LED重复同样的操作，直到最后一个LED关闭。这一过程反复进行，以实现流水的“运动”。

项目现象：

项目代码上传成功后，利用USB线上电，可以看到的现象是：10个LED将从左到右点亮，然后从右到左返回。

![](media/912e2c3f88b522b89b9935548bae3bd9.png)

### 项目 08：一位数码管

项目介绍：

七段数码管是一种显示十进制数字的电子显示设备，广泛应用于数字时钟、电子仪表、基本计算器和其他显示数字信息的电子设备。甚至我们在电影中看到的炸弹也有七段数码管。也许七段数码管看起来不够现代，但它们是更复杂的点阵显示器的替代品，在有限的光线条件下和强烈的阳光下都很容易使用。在这个项目中，我们将使用树莓派Pico板控制一位数码管显示数字。

项目元件：

|  ![](media/b18fe281156b29c44796f72222718d58.jpeg)  |  ![](media/2762753d227ba94de9f6e5c9ff79fe53.png)  |  ![](media/3ac518b4caa5086041545c60c7a6a2d1.png)  |  ![](media/75e38d601750a4707369bc73d8028063.png)  |
|-|-|-|-|
| 树莓派Pico板*1 | 树莓派Pico板的扩展板*1 | 公对母杜邦线若干 | 一位数码管*1 |
|  ![](media/098a2730d0b0a2a4b2079e0fc87fd38b.png)  |  ![](media/e380dd26e4825be9a768973802a55fe6.png)  |  ![](media/7dcbd02995be3c142b2f97df7f7c03ce.png)  |  |
| 220Ω电阻*8 | 面包板*1 | USB 线*1 |  |

元件知识：

![](media/16f7ea6e5316aa1ba8faee0b47bff294.png)

一位数码管显示原理：数码管显示是一种半导体发光器件。它的基本单元是一个发光二极管(LED)。数码管显示根据段数可分为7段数码管和8段数码管。8段数码管比7段多一个LED单元(用于小数点显示)。七段LED显示屏的每段是一个单独的LED。根据LED单元接线方式，数码管可分为共阳极数码管和共阴极书案管。

在共阴极7段数码管中，分段LED的所有阴极(或负极)都连接在一起，你应该把共阴极连接到GND，要点亮一个分段LED，你可以将其关联的引脚设置为HIGH。

在共阳极7段数码管中，所有段的LED阳极(正极)都连接在一起，你应该把共阳极连接到+5V。要点亮一个分段LED，你可以将其关联的引脚设置为LOW。

![](media/570d533e2307bf68aee145d544ab5939.png)

数码管的每个部分由一个LED组成。所以当你使用它的时候，你也需要使用一个限流电阻。否则，LED会被烧坏。在这个实验中，我们使用了一个普通的共阴极一位数码管。正如我们上面提到的，你应该将公共阴极连接到GND。要点亮一个分段LED，你可以将其关联的引脚设置为HIGH。

项目电路图和接线图：

![](media/cdecc109f3d703edb67b372d8c05a696.png)

注意：插入面包板的七段数码管方向与接线图一致，右下角多一个点。

![](media/4429ccfb9623a9a3975cdf217c927204.png)

![](media/1aea8db87a3e0929caecb82b4b4690dc.png)

项目代码：

数字显示分7段，小数点显示分1段。当显示某些数字时，相应的段将被点亮。例如，当显示数字1时，b和c段将被打开。


```C
/* 
 * 文件名 : 一位数码管
 * 描述 : 一位数字管显示数字从9到0.
 * 作者 : www.keyes-robot.com
*/
// 设置每个段的IO引脚
int a=17; // a段的数字引脚GP17
int b=16; // b段的数字引脚GP16
int c=14; // c段的数字引脚GP14
int d=13; // d段的数字引脚GP13
int e=12; // e段的数字引脚GP12
int f=18; // f段的数字引脚GP18
int g=19; // g段的数字引脚GP19
int dp=15; // dp段的数字引脚GP15
void digital_0(void) // 显示数字0
{
  digitalWrite(a,HIGH);
  digitalWrite(b,HIGH);
  digitalWrite(c,HIGH);
  digitalWrite(d,HIGH);
  digitalWrite(e,HIGH);
  digitalWrite(f,HIGH);
  digitalWrite(g,LOW);
  digitalWrite(dp,LOW);
}
void digital_1(void) // 显示数字1
{
  digitalWrite(a,LOW);
  digitalWrite(b,HIGH);
  digitalWrite(c,HIGH);
  digitalWrite(d,LOW);
  digitalWrite(e,LOW);
  digitalWrite(f,LOW);
  digitalWrite(g,LOW);
  digitalWrite(dp,LOW);
}
void digital_2(void) // 显示数字2
{
  digitalWrite(a,HIGH);
  digitalWrite(b,HIGH);
  digitalWrite(c,LOW);
  digitalWrite(d,HIGH);
  digitalWrite(e,HIGH);
  digitalWrite(f,LOW);
  digitalWrite(g,HIGH);
  digitalWrite(dp,LOW);
}
void digital_3(void) // 显示数字3
{
  digitalWrite(a,HIGH);
  digitalWrite(b,HIGH);
  digitalWrite(c,HIGH);
  digitalWrite(d,HIGH);
  digitalWrite(f,LOW);
  digitalWrite(e,LOW);
  digitalWrite(dp,LOW);
  digitalWrite(g,HIGH);
}
void digital_4(void) // 显示数字4
{
  digitalWrite(a,LOW);
  digitalWrite(b,HIGH);
  digitalWrite(c,HIGH);
  digitalWrite(d,LOW);
  digitalWrite(e,LOW);
  digitalWrite(f,HIGH);
  digitalWrite(g,HIGH);
  digitalWrite(dp,LOW);
}
void digital_5(void) // 显示数字5
{
  digitalWrite(a,HIGH);
  digitalWrite(b,LOW);
  digitalWrite(c,HIGH);
  digitalWrite(d,HIGH);
  digitalWrite(e,LOW);
  digitalWrite(f,HIGH);
  digitalWrite(g,HIGH);
  digitalWrite(dp,LOW);
}
void digital_6(void) // 显示数字 6
{
  digitalWrite(a,HIGH);
  digitalWrite(b,LOW);
  digitalWrite(c,HIGH);
  digitalWrite(d,HIGH);
  digitalWrite(e,HIGH);
  digitalWrite(f,HIGH);
  digitalWrite(g,HIGH);
  digitalWrite(dp,LOW);
}
void digital_7(void) // 显示数字 7
{
  digitalWrite(a,HIGH);
  digitalWrite(b,HIGH);
  digitalWrite(c,HIGH);
  digitalWrite(d,LOW);
  digitalWrite(e,LOW);
  digitalWrite(f,LOW);
  digitalWrite(g,LOW);
  digitalWrite(dp,LOW);
}
void digital_8(void) // 显示数字 8
{
  digitalWrite(a,HIGH);
  digitalWrite(b,HIGH);
  digitalWrite(c,HIGH);
  digitalWrite(d,HIGH);
  digitalWrite(e,HIGH);
  digitalWrite(f,HIGH);
  digitalWrite(g,HIGH);
  digitalWrite(dp,LOW);
}
void digital_9(void) // 显示数字 9
{
  digitalWrite(a,HIGH);
  digitalWrite(b,HIGH);
  digitalWrite(c,HIGH);
  digitalWrite(d,HIGH);
  digitalWrite(e,LOW);
  digitalWrite(f,HIGH);
  digitalWrite(g,HIGH);
  digitalWrite(dp,LOW);
}
void setup()
{
  int i;// 声明一个变量
  for(i=12;i<=19;i++)
  pinMode(i,OUTPUT);// 设置PIN 12-19为输出
}
void loop()
{
  while(1)
  {
    digital_9();// 显示数字 9
    delay(1000); // 延时1秒
    digital_8();// 显示数字 8
    delay(1000); // 延时1秒
    digital_7();// 显示数字 7
    delay(1000); // 延时1秒
    digital_6();// 显示数字 6
    delay(1000); // 延时1秒
    digital_5();// 显示数字 5
    delay(1000); // 延时1秒
    digital_4();// 显示数字 4
    delay(1000); 
    digital_3();// 显示数字 3
    delay(1000); 
    digital_2();// 显示数字 2
    delay(1000); 
    digital_1();// 显示数字 1
    delay(1000); 
    digital_0();// 显示数字 0
    delay(1000);
  }
}
```

项目现象：

项目代码上传成功后，利用USB线上电，可以看到的现象是：一位数码管将显示从9到0的数字。

### 项目 09：四位数码管

项目介绍：

四位数码管是一种非常实用的显示器件。电子时钟的显示，球场上的记分员，公园里的人数都是需要的。由于价格低廉，使用方便，越来越多的项目将使用4位数码管。在这个项目中，我们使用树莓派Pico板控制四位数码管来显示四位数字。

项目元件：

|  ![](media/b18fe281156b29c44796f72222718d58.jpeg)  |  ![](media/2762753d227ba94de9f6e5c9ff79fe53.png)  |  ![](media/5db81e1951367d0502ee2075f762ac4e.png)  |
|-|-|-|
| 树莓派Pico板*1 | 树莓派Pico板的扩展板*1 | 4P 转杜邦线母单20厘米*1 |
|  ![](media/d608b9e7f824c5c857f9e35ed4fa061e.png)  |  ![](media/7dcbd02995be3c142b2f97df7f7c03ce.png)  |  |
| 四位数码管模块*1 | USB 线*1 |  |

元件知识：

TM1650四位数码管：是一个12脚的带时钟点的四位共阳数码管（0.36英寸）的显示模块，驱动芯片为TM1650，只需2根信号线即可使单片机控制四位数码管。控制接口电平可为5V或3.3V。

4位数码管模块规格参数：

工作电压：DC 3.3V-5V

最大电流：100MA

最大功率：0.5W

4位数码管模块原理图：

![](media/5f400887c90fc00098a3e77beca656ef.png)

项目电路图和接线图：

![](media/352d6413bc6efcb12128a7742459ea43.png)

![](media/8cc6b6598eced32eef3942311eea86ec.png)

添加TM1650库：

本项目代码使用了一个名为“TM1650”库。如果你还没有添加它，请在学习之前先添加。添加第三方库的步骤如下:

打开Arduino IDE，单击“项目”→“加载库”→“添加 .ZIP库...”。在弹出窗口中找到该目录下名为：4. Arduino 教程\1. Windows 系统\3.库文件\TM1650.ZIP的文件。先单击“TM1650.ZIP”文件，再单击“打开”。

![](media/64b5fe6148c6636c47e2b0c51966d759.png)

![](media/98a2a1573c8505811eb07a6681328828.png)

项目代码：

TM1650库添加完之后，你可以打开我们提供的代码：

```C
/* 
 * 文件名  : 四位数码管
 * 描述 : 四位数管显示数字从1111到9999.
 * 作者 : www.keyes-robot.com
*/
#include "TM1650.h"
#define CLK 21    //用于TM1650的引脚定义，可以更改为其他端口 
#define DIO 20
TM1650 DigitalTube(CLK,DIO);

void setup(){
  //DigitalTube.setBrightness();  //亮度从0到7(默认为2)
  //DigitalTube.displayOnOFF();   // 0= off,1= on(默认是 1)
  for(char b=1;b<5;b++){
    DigitalTube.clearBit(b);      //要清除哪一部分
  }
  DigitalTube.displayDot(1,true); // 显示第一个数字
  DigitalTube.displayDot(2,true);
  DigitalTube.displayDot(3,true);
  DigitalTube.displayDot(4,true);
  DigitalTube.displayBit(3,0);    //显示哪个数字, 位= 1 - 4,数量= 0 - 9
}

void loop(){
  for(int num=0; num<10; num++){
    DigitalTube.displayBit(1,num);
    DigitalTube.displayBit(2,num);
    DigitalTube.displayBit(3,num);
    DigitalTube.displayBit(4,num);
    delay(1000);
  }  
 }
```

项目现象：

项目代码上传成功后，利用USB线上电，你会看到的现象是：四位数码管显示数字0000-9999，并在一个无限循环中重复这些动作。

### 项目 10：8×8 点阵显示

项目介绍：

点阵屏是一种电子数字显示设备，可以显示机器、钟表、公共交通离场指示器和许多其他设备上的信息。在这个项目中，我们将使用树莓派Pico板控制8x8 LED点阵来显示“❤”图案。

项目元件：

|  ![](media/b18fe281156b29c44796f72222718d58.jpeg)  |  ![](media/2762753d227ba94de9f6e5c9ff79fe53.png)  |  ![](media/5db81e1951367d0502ee2075f762ac4e.png)  |
|-|-|-|
| 树莓派Pico板*1 | 树莓派Pico板的扩展板*1 | 4P 转杜邦线母单20厘米*1 |
|  ![](media/09e0ddbf584131e9bc34ff474d52f4d0.png)  |  ![](media/7dcbd02995be3c142b2f97df7f7c03ce.png)  |  |
| 8*8点阵屏模块*1 | USB 线*1 |  |

元件知识：

8\*8点阵屏模块：8\*8的点阵由64个LED组成，每个LED被放置在一排和一列的交叉点上。利用单片机驱动一个8\*8点阵时，我们总共需要用到16个数字口，这样就极大的浪费单片机资料。为此，我们特别设计了这个模块，利用HT16K33芯片驱动1个8\*8点阵，只需要利用单片机的I2C通信端口控制点阵，大大的节约了单片机资源。

8\*8点阵屏模块规格参数：

工作电压：DC 5V

电流：200MA

最大功率：1W

8\*8点阵屏模块原理图：

![](media/b04fe5e60695365a23644395aaef5085.png)

有些模块上自带3个拨码开关，可以让你随意拨动开关，这是用来设置I2C通信地址的，设置方法如下表格。我们的这个模块中，模块已经固定了通信地址，A0，A1，A2全部接地，即地址为0x70。

|A0（1）|A1（2）|A2（3）|A0（1）|A1（2）|A2（3）|A0（1）|A1（2）|A2（3）|
|-|-|-|-|-|-|-|-|-|
|0（OFF）|0（OFF）|0（OFF）|1（ON）|0（OFF）|0（OFF）|0（OFF）|1（ON）|0（OFF）|
|0X70|0X71|0X72| | | | | | |
|A0（1）|A1（2）|A2（3）|A0（1）|A1（2）|A2（3）|A0（1）|A1（2）|A2（3）|
|1（ON）|1（ON）|0（OFF）|0（OFF）|0（OFF）|1（ON）|1（ON）|0（OFF）|1（ON）|
|0X73|0X74|0X75|  | | | | | |
|A0（1）|A1（2）|A2（3）|A0（1）|A1（2）|A2（3）| | | |
|0（OFF）|1（ON）|1（ON）|1（ON）|1（ON）|1（ON）| | | |
|0X76|0X77| | | | | | | |

项目电路图和接线图：

![](media/20cf7e805b14e17ce67af366dbf61b9e.png)
    
![](media/b7257311451a3131f4c2e705237ecf9b.png)

添加Matrix库：

本项目代码使用了一个名为“Matrix”库。如果你还没有添加它，请在学习之前先添加，添加第三方库的步骤如下：

打开Arduino IDE，单击“项目”→“加载库”→“添加 .ZIP库...”。在弹出窗口中找到该目录下名为：4. Arduino 教程\1. Windows 系统\3.库文件\Matrix.ZIP的文件，先单击“Matrix.ZIP”文件，再单击“打开”。

![](media/4946cc65c46c6437fbfb6a4dbcefe9d7.png)

![](media/65b5c0ebbfc400c171b12c7fd1a160df.png)

项目代码：

Matrix库添加完之后，你可以打开我们提供的代码：

```C
/*
 * 文件名 : 8×8点阵显示
 * 描述 : 8x8点阵显示“心形”图案.
 * 作者 : www.keyes-robot.com
*/
#include <Matrix.h>
Matrix myMatrix(20,21);
uint8_t LedArray1[8]={0x00,0x18,0x24,0x42,0x81,0x99,0x66,0x00};
uint8_t  LEDArray[8];
void setup(){
myMatrix.begin(0x70);
}

void loop(){
  myMatrix.clear();
  for(int i=0; i<8; i++)
  {
    LEDArray[i]=LedArray1[i];
    for(int j=7; j>=0; j--)
    {
      if((LEDArray[i]&0x01)>0)
      myMatrix.drawPixel(j, i,1);
      LEDArray[i] = LEDArray[i]>>1;
    }
  }
  myMatrix.writeDisplay();
}
```

项目现象：

项目代码上传成功后，利用USB线上电，你会看到的现象是：8\*8点阵屏显示“❤”图案。

### 项目 11：74HC595N 控制 8 个灯 

项目介绍：

在之前的项目中，我们已经学过了怎样点亮一个LED。

树莓派Pico板上只有26个IO端口，我们如何点亮大量的led呢?
有时可能会耗尽树莓派Pico板上的所有引脚，这时候就需要用移位寄存器扩展它。你可以使用74HC595N芯片一次控制8个输出，而只占用你的微控制器上的几个引脚。你还可以将多个寄存器连接在一起，以进一步扩展输出，在这个项目中，我们将使用树莓派Pico板，74HC595芯片和LED制作一个流水灯来了解74HC595芯片的功能。

项目元件：

|  ![](media/b18fe281156b29c44796f72222718d58.jpeg)  |  ![](media/2762753d227ba94de9f6e5c9ff79fe53.png)  |  ![](media/f97e58ab51ec0a274ff3e72e08a7d55d.png)  |
|-|-|-|
| 树莓派Pico板*1 | 树莓派Pico板的扩展板*1 | 74HC595N芯片*1 |
|  ![](media/098a2730d0b0a2a4b2079e0fc87fd38b.png)  |  ![](media/e380dd26e4825be9a768973802a55fe6.png)  |  ![](media/c801a7baee258ff7f5f28ac6e9a7097b.png)  |
| 220Ω电阻*8 | 面包板*1 | 跳线若干 |
|  ![](media/3ec5906fad2172708d449390140f55e6.png)  |  ![](media/7dcbd02995be3c142b2f97df7f7c03ce.png)  |  ![](media/3ac518b4caa5086041545c60c7a6a2d1.png)  |
| 红色LED*8 | USB 线*1 | 公对母杜邦线若干 |

元件知识：

![](media/6921c6d60135e072ed4bd24564ec4a6d.png)

74HC595N芯片：简单来说就是具有8
位移位寄存器和一个存储器，以及三态输出功能。移位寄存器和存储器同步于不同的时钟，数据在移位寄存器时钟SCK的上升沿输入，在存储寄存器时钟RCK的上升沿进入的存储寄存器中去。如果两个时钟连在一起，则移位寄存器总是比存储寄存器早一个脉冲。移位寄存器有一个串行移位输入端（SI）和一个用于级联的串行输出端（SQH）,8位移位寄存器可以异步复位（低电平复位），存储寄存器有一个8位三态并行的总线输出，当输出使能（OE）被使能（低电平有效）将存储寄存器中输出至74HC595N的引脚（总线）。

![](media/858b189f06ad68afe051b15043b2affd.png)

引脚说明：

| 13引脚OE | 是一个输出使能引脚，用于确保锁存器的数据是否输入到Q0-Q7引脚。在低电平时，不输出高电平。在本实验中，我们直接连接GND，保持低电平输出数据。 |
|-|-|
| 14引脚SI | 这是74HC595接收数据的引脚，即串行数据输入端，一次只能输入一位，那么连续输入8次，就可以组成一个字节了。 |
| 10引脚SCLR | 一个初始化存储寄存器管脚的管脚。在低电平时初始化内部存储寄存器。在这个实验中，我们连接VCC以保持高水平。 |
| 11引脚SCK | 移位寄存器的时钟引脚，上升沿时，移位寄存器中的数据整体后移，并接收新的数据输入 |
| 12引脚RCK | 存储寄存器的时钟输入引脚。上升沿时，数据从移位寄存器转存到存储寄存器中。这时数据就从Q0~Q7端口并行输出。 |
| 9引脚SQH | 引脚是一个串行输出引脚，专门用于芯片级联，接下一个74HC595的SI端 |
| Q0--Q7(15引脚，1-7引脚) | 八位并行输出端，可以直接控制数码管的8个段 |

项目电路图和接线图：

![](media/1738cecf584c83b55370153ebc1688b7.png)

注意：需要注意74HC595N芯片插入的方向

![](media/ec1ecffbdf054501b67abeeb96917e73.png)

![](media/5a0de137092d094f6007098ac141586a.png)

![](media/c96c92db4125b2cc518a0ed5f72e33f9.png)

项目代码：

```C
/* 
 * 文件名 : 74HC595N 控制 8 个灯
 * 描述 : 使用74HC575N驱动8个led显示流动光.
 * 作者 : www.keyes-robot.com
*/
int dataPin = 18;   // 引脚接74HC595的DS(GP14)  
int latchPin = 20;  // 引脚连接到ST_CP的74HC595(GP12)
int clockPin = 21;  // 引脚连接到74HC595的SH_CP(GP11)          

void setup() { // 将引脚设置为输出
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
}

void loop() {
  // 定义一个单字节变量，用这8位表示LED条形图中8个LED的状态.
  // 这个变量被赋值给0x01，也就是二进制的00000001，这表示只有一个LED灯亮着.
  byte x = 0x01;    // 0b 0000 0001
  for (int j = 0; j < 8; j++) { // 让led从右到左点亮
    writeTo595(LSBFIRST, x);
    x <<= 1; // 使变量向左移动一位，然后明亮的LED向左移动一步.
    delay(100);
  }
  delay(100);
  x = 0x80;       //0b 1000 0000
  for (int j = 0; j < 8; j++) { // 让led从左到右点亮
    writeTo595(LSBFIRST, x);
    x >>= 1;    
    delay(100);
  }
  delay(100);
}
void writeTo595(BitOrder order, byte _data ) {
  // 输出低电平到latchPin
  digitalWrite(latchPin, LOW);
  // 发送串行数据到74HC595
  shiftOut(dataPin, clockPin, order, _data);
  // 输出高电平到latchPin, 74HC595将数据更新到并行输出端口.
  digitalWrite(latchPin, HIGH);
}
```

项目现象：

项目代码上传成功后，利用USB线上电，可以看到的现象是：8个LED开始以流水模式闪烁。

### 项目 12：有源蜂鸣器

项目介绍：

有源蜂鸣器是一个发声组件。它被广泛用作电脑、打印机、报警器、电子玩具、电话、计时器等的发声元件。它有一个内在的振动源，只需连接5V电源，即可持续发出嗡嗡声。在这个项目中，我们将使用树莓派Pico板控制有源蜂鸣器发出“滴滴”声。

项目元件：

|  ![](media/2db038cb38e0beabf2f9e14043d23a6f.jpeg)  |  ![](media/f39510c60d4c43dfca4e26b0bdb9aae7.png)  |  ![](media/e380dd26e4825be9a768973802a55fe6.png)  |
|-|-|-|
| 树莓派Pico板*1 | 树莓派Pico板的扩展板*1 | 面包板*1 |
|  ![](media/9197d4aff9356c585b7ef68e33a6881d.png)  |  ![](media/098a2730d0b0a2a4b2079e0fc87fd38b.png)  |  ![](media/c801a7baee258ff7f5f28ac6e9a7097b.png)  |
| NPN型晶体管(S8050)*1 | 1kΩ电阻*1 | 跳线若干 |
|  ![](media/4b4f653a76a82a3b413855493cc58fba.png)  |  ![](media/7dcbd02995be3c142b2f97df7f7c03ce.png)  |  ![](media/3ac518b4caa5086041545c60c7a6a2d1.png)  |
| 有源蜂鸣器*1 | USB 线*1 | 公对母杜邦线若干 |

元件知识：

![](media/11ec5ddc982db9928341e858aab94652.png)

有源蜂鸣器内部有一个简单的振荡器电路，可以将恒定的直流电转换成特定频率的脉冲信号。一旦有源蜂鸣器收到一个高电平，它将产生声音。而无源蜂鸣器是一种内部没有振动源的集成电子蜂鸣器，它必须由2K-5K方波驱动，而不是直流信号。这两个蜂鸣器的外观非常相似，但是一个带有绿色电路板的蜂鸣器是无源蜂鸣器，而另一个带有黑色胶带的是有源蜂鸣器。无源蜂鸣器不能区分正极性而有源极性蜂鸣器是可以。如下所示：

![](media/76d53f3b35afaa98712e855302e44e32.png)

![](media/9197d4aff9356c585b7ef68e33a6881d.png)

晶体管:

由于蜂鸣器需要很大的电流，ESP32输出能力的GPIO不能满足要求，这里需要一个NPN型晶体管来放大电流。晶体管，全称:半导体晶体管，是一种控制电流的半导体器件。晶体管可以用来放大微弱信号，也可以用作开关。它有三个电极(pin)：基极(b)，集电极(c)和发射极(e)。当电流通过“be”之间时，“ce”将允许几倍的电流通过(晶体管放大)，此时，晶体管在放大区工作。当“be”之间的电流超过某个值时，“ce”将不再允许电流增加，此时晶体管工作在饱和区。晶体管有两种类型如下所示：PNP和NPN

![](media/02dad9f2fcac0d7bfe4cc135d2301aa6.png)

PNP晶体管 NPN晶体管

在我们的套件中，PNP晶体管标记为8550,NPN晶体管标记为8050。

基于晶体管的特性，它常被用作数字电路中的开关。由于单片机输出电流的能力很弱，我们将使用晶体管来放大电流和驱动大电流的元件。在使用NPN晶体管驱动蜂鸣器时，通常采用以下方法：如果GPIO输出高电平，电流将流过R1，晶体管将传导，蜂鸣器将发出声音。如果GPIO输出低电平，没有电流流过R1，晶体管就不会传导，蜂鸣器也不会响。在使用PNP晶体管驱动蜂鸣器时，通常采用以下方法：如果GPIO输出低电平，电流将流过R1，晶体管将传导，蜂鸣器将发出声音。如果GPIO输出高电平，没有电流流过R1，晶体管就不会传导，蜂鸣器也不会响。

![](media/e33aa5534aa1bd11e4ca2fa51c5b6548.png)

项目电路图和接线图：

![](media/8b84654a0f04d7e4c5f9c2f2923e577b.png)

![](media/6e607b53416c90c3573d3f890f8a5ed0.png)

注意：1.该电路中蜂鸣器的电源为5V。在3.3V的电源下，蜂鸣器可以工作，但会降低响度。

2.VUSB应连接到USB线的正极，如果它连接到GND，它可能烧坏电脑或树莓派Pico板。同样，树莓派Pico板的36-40引脚接线时也要小心，避免短路。

3.有源蜂鸣器正极(“+”/长引脚)接引脚16，负极（短引脚）接GND。

项目代码：

```C
/* 
 * 文件名 : 有源蜂鸣器
 * 描述 : 有源蜂鸣器发出滴滴声.
 * 作者 : www.keyes-robot.com
*/
#define buzzerPin  16   //蜂鸣器引脚定义

void setup ()
{
  pinMode (buzzerPin, OUTPUT);
}
void loop ()
{
  digitalWrite (buzzerPin, HIGH);
  delay (500);
  digitalWrite (buzzerPin, LOW);
  delay (500);
}
```

项目现象：

项目代码上传成功后，利用USB线上电，可以看到的现象是：有源蜂鸣器发出“滴滴”声。

### 项目 13：无源蜂鸣器

项目介绍

在之前的项目中，我们研究了有源蜂鸣器，它只能发出一种声音，可能会让你觉得很单调。这个项目将学习另一种蜂鸣器，无源蜂鸣器。与有源蜂鸣器不同，无源蜂鸣器可以发出不同频率的声音。在这个项目中，你将使用树莓派Pico板控制无源蜂鸣器发出警报声。

项目元件：

|  ![](media/e796f897747c8b2786364a0a1b18fe07.jpeg)  |  ![](media/48434ce34b111ef48f78bd7f777dbcfd.png)  |  ![](media/e380dd26e4825be9a768973802a55fe6.png)  |
|-|-|-|
| 树莓派Pico板*1 | 树莓派Pico板的扩展板*1 | 面包板*1 |
|  ![](media/9197d4aff9356c585b7ef68e33a6881d.png)  |  ![](media/098a2730d0b0a2a4b2079e0fc87fd38b.png)  |  ![](media/aa06e883415a425c727a539a8267ac7a.png)  |
| NPN型晶体管(S8050)*1 | 1kΩ电阻*1 | 跳线若干 |
|  ![](media/d1ea1bb2b2749820cab389d5b85b838b.png)  |  ![](media/7dcbd02995be3c142b2f97df7f7c03ce.png)  |  ![](media/3ac518b4caa5086041545c60c7a6a2d1.png)  |
| 无源蜂鸣器*1 | USB 线*1 | 公对母杜邦线若干 |

元件知识：

![](media/c32485f7091bcc1addb8e3c3d86cf3df.png)

无源蜂鸣器是一种内部没有振动源的集成电子蜂鸣器。它必须由2K-5K方波驱动，而不是直流信号。这两个蜂鸣器的外观非常相似，但是一个带有绿色电路板的蜂鸣器是无源蜂鸣器，而另一个带有黑色胶带的是有源蜂鸣器。无源蜂鸣器不能区分正极性而有源极性蜂鸣器是可以。

![](media/fc42c5ed014609ff0b290ee5361bb2fd.png)

晶体管: 请参考项目12.

项目电路图和接线图:

![](media/88e1667cdd09ffa9394f7aae9ce60242.png)

![](media/0257f2526dcedd215a11ccbce45ded51.png)

项目代码：

```C
/*
 * 文件名 : 无源蜂鸣器
 * 描述 : 无源蜂鸣器发出警报.
 * 作者 : www.keyes-robot.com
*/
#define PIN_BUZZER 16   //蜂鸣器引脚定义

void setup() {
  pinMode(PIN_BUZZER, OUTPUT);
}

void loop() {
    alert();
}

void alert() {
  float sinVal;         // 定义一个变量来保存sinVal
  int toneVal;          // 定义一个变量来保存声音频率
  for (int x = 0; x < 360; x += 10) {     // X从0-360
    sinVal = sin(x * (PI / 180));       // 计算sinx
    toneVal = 2000 + sinVal * 500;      // 根据sinx计算声音的频率
    freq(PIN_BUZZER, toneVal, 10);
  }
}

void freq(int PIN, int freqs, int times) {
  if (freqs == 0) {
    digitalWrite(PIN, LOW);
  }
  else {
    for (int i = 0; i < times * freqs / 1000; i ++) {
      digitalWrite(PIN, HIGH);
      delayMicroseconds(1000000 / freqs / 2);
      digitalWrite(PIN, LOW);
      delayMicroseconds(1000000 / freqs / 2);
    }
  }
}
```

项目现象：

项目代码上传成功后，利用USB线上电，可以看到的现象是：无源蜂鸣器发出警报声。

### 项目 14：小台灯

项目介绍：

你知道树莓派Pico板可以在你按下按键的时候点亮LED吗?

在这个项目中，我们将使用树莓派Pico板，一个按键开关和一个LED来制作一个迷你台灯。

项目代码：

|  ![](media/d7bde912c31f86d5e3854e608a5ff08f.jpeg)  |  ![](media/2762753d227ba94de9f6e5c9ff79fe53.png)  |  ![](media/5b8fea4657b47510d199f740fdcaaa9d.png)  |  ![](media/ef77f5a64c382157fc2dea21ec373fef.png)  |  ![](media/da8a2a9d15baf7280966f3fdbb025a8c.png)  |
|-|-|-|-|-|
| 树莓派Pico板*1 | 树莓派Pico板的扩展板*1 | 按键*1 | 红色 LED*1 | 10KΩ电阻*1 |
|  ![](media/e380dd26e4825be9a768973802a55fe6.png)  |  ![](media/845d05a6108b1662b828610ba9dcb788.png)  |  ![](media/7dcbd02995be3c142b2f97df7f7c03ce.png)  |  ![](media/b006569d007392f7ebcd1a2d84c0c942.png)  |  ![](media/9cab81f7da18c7b0c245ec2a2f614f3a.png)  |
| 面包板*1 | 220Ω电阻*1 | USB 线*1 | 公对母杜邦线若干 | 按键帽*1 |

元件知识：

![](media/5b8fea4657b47510d199f740fdcaaa9d.png)

按键：按键可以控制电路的通断，把按键接入电路中，不按下按键的时候电路是断开的，

一按下按键电路就通啦，但是松开之后就又断了。可是为什么按下才通电呢？这得从按键的内部构造说起。没按下之前，电流从按键的一端过不去另一端；按下的时候，按键内部的金属片把两边连接起来让电流通过。

按键内部结构如图：![](media/d2a204e61c768f18924150db58aee093.png)，未按下按键之前，1、2就是导通的，3、4也是导通的，但是1、3或1、4或2、3或2、4是断开（不通）的；只有按下按键时，1、3或1、4或2、3或2、4才是导通的。

在设计电路时，按键开关是最常用的一种元件。

按键的原理图: 

![](media/5e42fde9876f9be810d85a7fb8b331f7.png)![](media/8677548f9e756281629430d66ba3a460.png)  

什么是按键抖动？

我们想象的开关电路是“按下按键-立刻导通”“再次按下-立刻断开”，而实际上并非如此。

按键通常采用机械弹性开关，而机械弹性开关在机械触点断开闭合的瞬间（通常
10ms左右），会由于弹性作用产生一系列的抖动，造成按键开关在闭合时不会立刻稳定的接通电路，在断开时也不会瞬时彻底断开。

![](media/4dbcca62c2d75cab03260584924a16d8.jpeg)

那又如何消除按键抖动呢？

常用除抖动方法有两种：软件方法和硬件方法。这里重点讲讲方便简单的软件方法。

我们已经知道弹性惯性产生的抖动时间为10ms左右，用延时命令推迟命令执行的时间就可以达到除抖动的效果。

所以我们在代码中加入了0.02秒的延时以实现按键防抖的功能。

![](media/1497573e05f993b5f32923fcd6590a01.jpeg)

项目电路图和接线图：

![](media/19f9dcfaacc32a6bc8ae16e362aa3122.png)

![](media/1c75a05a53faacfca9592631619909b0.png)

注意:

怎样连接LED

![](media/14a84d5f016d7566151a5563c502787e.png)

怎样识别五色环220Ω电阻和五色环10KΩ电阻

![](media/793740d0b936e516ca354111e2d0eb79.png)

![](media/18484e5d16b6d89c63825cc2efa6a543.png)

项目代码：

```C
/* 
 * 文件名  : 小台灯
 * 描述 : 模拟小台灯.
 * 作者 : www.keyes-robot.com
*/
#define PIN_LED    19
#define PIN_BUTTON 22
bool ledState = false;

void setup() {
  // 初始化数字引脚PIN_LED作为输出.
  pinMode(PIN_LED, OUTPUT);
  pinMode(PIN_BUTTON, INPUT);
}

// 循环函数会一直循环下去
void loop() {
  if (digitalRead(PIN_BUTTON) == LOW) {
    delay(20);
    if (digitalRead(PIN_BUTTON) == LOW) {
      reverseGPIO(PIN_LED);
    }
    while (digitalRead(PIN_BUTTON) == LOW);
  }
}

void reverseGPIO(int pin) {
  ledState = !ledState;
  digitalWrite(pin, ledState);
}
```

项目现象：

项目代码上传成功后，利用USB线上电，可以看到的现象是：按下按钮，LED亮起；当按钮松开时，LED仍亮着。再次按下按钮，LED熄灭；当按钮释放时，LED保持关闭。是不是很像个小台灯？

### 项目 15：模拟沙漏

项目介绍：

古代人没有电子时钟，就发明了沙漏来测时间，沙漏两边的容量比较大，在一边装了细沙，中间有个很小的通道，将沙漏直立，有细沙的一边在上方，由于重力的作用，细沙就会往下流通过通道到沙漏的另一边，当细沙都流到下边了，就倒过来，把一天反复的次数记录下来，第二天就可以通过沙漏反复流动的次数而知道这一天大概的时间了。这一课我们将利用树莓派Pico板控制倾斜开关和LED灯来模拟沙漏，制作一个电子沙漏。

项目元件：

|  ![](media/222aee34a428755aaf97b711ded3f09a.jpeg)  |  ![](media/a148c40620767714cde78f41a4822528.png)  |  ![](media/36f15610f430e5d5138f4e4fb721c40f.png)  |  ![](media/ef77f5a64c382157fc2dea21ec373fef.png)  |
|-|-|-|-|
| 树莓派Pico板*1 | 树莓派Pico板的扩展板*1 | 倾斜开关*1 | 红色 LED*4 |
|  ![](media/e380dd26e4825be9a768973802a55fe6.png)  |  ![](media/845d05a6108b1662b828610ba9dcb788.png)  |  ![](media/7dcbd02995be3c142b2f97df7f7c03ce.png)  |  |
| 面包板*1 | 220Ω电阻*4 | USB 线*1 |  |
|  ![](media/da8a2a9d15baf7280966f3fdbb025a8c.png)  |  ![](media/e9a8d050105397bb183512fb4ffdd2f6.png)  |  ![](media/3ac518b4caa5086041545c60c7a6a2d1.png)  |  |
| 10KΩ电阻*1 | 跳线若干 | 公对母杜邦线若干 |  |

元件知识：

![](media/8c40739f8e05f753f145420b421a0f47.png)

倾斜开关也叫数字开关。里面有一个可以滚动的金属球。采用金属球滚动与底部导电板接触的原理来控制电路的通断。当倾斜开关是滚珠型倾斜感应单方向性触发开关，当倾斜传感器向触发端（两根金属脚端）倾斜时，倾斜开关处于闭路状态，模拟端口的电压约为5V(二进制数为1023)。这样，LED会亮起。当倾斜开关在水平位置或向另一端倾斜时，倾斜开关处于开路状态，模拟端口的电压约为0V(0二进制)。LED将会关闭。在程序中，我们根据模拟端口的电压值，是否大于2.5V(512二进制)来判断开关是开还是关。

这里用倾斜开关的内部结构来说明它是如何工作的，显示如下图：

![](media/40bc569b295c4656bd973da4ad8734e2.png)

项目电路图和接线图：

![](media/033b2d8368f73658fff3b00d8508e4f3.png)

![](media/157dda6c8dad94ceddcb2cea06f69f03.png)

注意:

怎样连接LED

![](media/14a84d5f016d7566151a5563c502787e.png)

怎样识别五色环220Ω电阻和五色环10KΩ电阻

![](media/793740d0b936e516ca354111e2d0eb79.png)

![](media/18484e5d16b6d89c63825cc2efa6a543.png)

项目代码：

```C
/* 
 * 文件名  : 模拟沙漏
 * 描述 : 倾斜开关和四个led模拟沙漏.
 * 作者 : www.keyes-robot.com
*/
#define SWITCH_PIN  22  // 倾斜开关连接到GP22
byte switch_state = 0;
void setup()
{
     for(int i=16;i<20;i++)
  {
        pinMode(i, OUTPUT);
  } 
    pinMode(SWITCH_PIN, INPUT);
 for(int i=16;i<20;i++)
  {
    digitalWrite(i,0);
  } 
  Serial.begin(9600);
}
void loop()
{
switch_state = digitalRead(SWITCH_PIN); 
Serial.println(switch_state);
 if (switch_state == 0) 
 {
 for(int i=16;i<20;i++)
  {
    digitalWrite(i,1);
    delay(500);
  } 
  }
   if (switch_state == 1) 
 {
   for(int i=19;i>15;i--)
   {
    digitalWrite(i,0);
    delay(500);
   }
  }
}
```

项目结果：

项目代码上传成功后，利用USB线上电，可以看到的现象是：将面包板倾斜到一定角度，led就会一个一个地亮起来。当回到上一个角度时，led会一个一个关闭。就像沙漏一样，随着时间的推移，沙子漏了出来。

### 项目 16：防窃警报器

项目介绍：

人体红外传感器测量运动物体发出的热的红外(IR)线。该传感器可以检测人、动物和汽车的运动，从而触发安全警报和照明。它们被用来检测移动，是安全的理想选择，如防盗警报和安全照明系统。在这个项目中，我们将使用树莓派Pico板控制人体红外传感器、蜂鸣器和LED来模拟防盗报警器。

项目元件：

|  ![](media/070789ba62f5044f894eb126635a43e5.jpeg)  |  ![](media/2762753d227ba94de9f6e5c9ff79fe53.png)  |  ![](media/239131563f68fbf379e97d8384cf2d9b.png)  |  ![](media/4b4f653a76a82a3b413855493cc58fba.png)  |
|-|-|-|-|
| 树莓派Pico板*1 | 树莓派Pico板的扩展板*1 | 人体红外传感器*1 | 有源蜂鸣器*1 |
|  ![](media/e380dd26e4825be9a768973802a55fe6.png)  |  ![](media/3ac518b4caa5086041545c60c7a6a2d1.png)  |  ![](media/845d05a6108b1662b828610ba9dcb788.png)  |  ![](media/7dcbd02995be3c142b2f97df7f7c03ce.png)  |
| 面包板*1 | 公对母杜邦线若干 | 220Ω电阻*1 | USB 线*1 |
|  ![](media/ef77f5a64c382157fc2dea21ec373fef.png)  |  ![](media/e9a8d050105397bb183512fb4ffdd2f6.png)  |  ![](media/9197d4aff9356c585b7ef68e33a6881d.png)  |  ![](media/098a2730d0b0a2a4b2079e0fc87fd38b.png)  |
| 红色 LED*1 | 跳线若干 | NPN型晶体管(S8050)*1 | 1kΩ电阻*1 |
|  ![](media/ebfacf0e05bcd3941fe3b641935756b0.png)  |  |  |  |
| 3Pin 杜邦母单*1 |  |  |  |

元件知识：

人体红外传感器：其原理是某些晶体，例如钽酸锂、硫酸三甘肽等受热时，晶体两端会产生数量相等、符号相反的电荷，将这些电荷经放大器可转换为电压输出。而人体是会释放红外线的，虽然比较微弱，但是还是可以检测得到的。人体红外传感器检测附近有人运动时，传感器信号端输出高电平1，否则输出低电平0。特别注意，这个传感器可以检测在运动中的人、动物和汽车，静止中的人、动物和汽车是检测不到的，检测最远距离大约为7米左右。

注意：人体红外传感器应避开日光、汽车头灯、白炽灯直接照射，也不能对着热源(如暖气片、加热器)或空调，以避免环境温度较大的变化而造成误报。同时还易受射频辐射的干扰。

传感器技术参数：

最大输入电压：DC 3.3 ~ 5 v

最大工作电流：50MA

最大功率：0.3W

工作温度：-20 ~ 85℃

输出高电平3V，低电平0 V

延迟时间：大约2.3到3秒钟

检测角度：大约100度

检测最远距离：大约 7米左右

指示灯输出(当输出高电平时，它将点亮)

引脚限制电流：50MA

传感器原理图：

![](media/9e1ec604aa6f9d4a3c1fe41d4bccd699.png)

项目电路图和接线图：

![](media/694b0402398d04cd0a42f449dcdd0f61.png)

![](media/a9539236b5a3cfbce85a0b67691083e2.png)

项目代码：

```C
/* 
 * 文件名 : 防窃警报器
 * 文件名 : 人体红外传感器,蜂鸣器和LED模拟防盗报警.
 * 作者 : www.keyes-robot.com
*/
#define buzzerPin   19   // 蜂鸣器的引脚
#define ledPin   22     // LED的引脚
#define pirPin   2     // 人体红外传感器的引脚
byte pirStat = 0;   // 人体红外传感器的状态
void setup() {
 pinMode(buzzerPin, OUTPUT); 
 pinMode(ledPin, OUTPUT);    
 pinMode(pirPin, INPUT);     
}
void loop()
{
 pirStat = digitalRead(pirPin); 
 if (pirStat == HIGH)
 {            // 如果发现有人或动物移动
   digitalWrite(buzzerPin, HIGH);  // 蜂鸣器蜂鸣
   digitalWrite(ledPin, HIGH);  // led灯点亮
   delay(500);
   digitalWrite(buzzerPin, LOW);  // 蜂鸣器不响
   digitalWrite(ledPin, LOW);  // led熄灭
   delay(500);
 } 
 else {
   digitalWrite(buzzerPin, LOW); // 如果没有发现人或动物在移动，关闭蜂鸣器
   digitalWrite(ledPin, LOW);  // led熄灭
 }
}
```

项目现象：

项目代码上传成功后，利用USB线上电，可以看到的现象是：如果人体红外传感器检测到附近有人移动时，蜂鸣器就会不断地发出警报，且LED不断地闪烁。

### 项目 17： I2C 128×32 LCD

项目介绍：

在生活中，我们可以利用显示器等模块来做各种实验。你也可以DIY各种各样的小物件。例如，用一个温度传感器和显示器做一个温度测试仪，或者用一个超声波模块和显示器做一个距离测试仪。下面，我们将使用LCD_128X32_DOT模块作为显示器，将其连接到树莓派Pico控制板上。将使用树莓派Pico主板控制LCD_128X32_DOT显示屏显示各种英文文字、常用符号和数字。

项目元件： 

|  ![](media/b1265f71184b5d144248ea3e847a18c9.jpeg)  |  ![](media/770714fabf173efb3ef34d9f84d09798.png)  |  ![](media/2762753d227ba94de9f6e5c9ff79fe53.png)  |
|-|-|-|
| 树莓派Pico板*1 | LCD_128X32_DOT*1 | 树莓派Pico板的扩展板*1 |
|  ![](media/e615c2849ce15bdfc8e26432004124ec.png)  |  ![](media/7dcbd02995be3c142b2f97df7f7c03ce.png)  |  |
| 4P 转杜邦线母单10厘米*1 | USB 线*1 |  |

元件知识：

![](media/770714fabf173efb3ef34d9f84d09798.png)

LCD_128X32_DOT：一个像素为128\*32的液晶屏模块，它的驱动芯片为ST7567A。模块使用IIC通信方式，同时，代码中包含所有英文字母和常用符号的库，可以直接调用。使用时，我们还可以在代码中设置，让英文字母和符号显示不同文字大小。为了方便设置图案显示，我们还提供一个取模软件，可将特定的图案转化成控制代码，然后直接复制到测试代码中使用的。

LCD_128X32_DOT原理图：

![](media/5451aed32bc5b7b30fbd5613ad09a65b.png)

LCD_128X32_DOT技术参数：

显示像素：128\*32 字符

芯片工作电压：4.5 ~ 5.5V

工作电流：100mA (5.0V)

模块最佳工作电压：5.0V

项目接线图：

特别注意：这里必须使用4P     转杜邦线母单10厘米连接LCD_128X32_DOT，LCD_128X32_DOT才会显示正常；否则，使用4P     转杜邦线母单20厘米，LCD_128X32_DOT可能会显示不正常。

![](media/37c015266b4998c76a04be21f0d0a969.png)

添加lcd128_32_io库：

本项目代码使用了一个名为“lcd128_32_io”库。如果你还没有添加，请在学习之前添加它。添加第三方库的步骤如下:

打开Arduino IDE，单击“项目”→“加载库”→“添加 .ZIP库...”。在弹出窗口中找到该目录下名为：4. Arduino 教程\1. Windows 系统\3.库文件\LCD_128X32.ZIP的文件，先选中LCD_128X32.ZIP文件，再单击“打开”。

![](media/82becdfbc8f63d7e75198b48fc42c382.png)

![](media/10cc58ee86e81a0abcbf26d4665606b6.png)

项目代码：

lcd128_32_io库添加完成后，你可以打开我们提供的代码：


```C
/*
 * 文件名 : LCD 128*32
 * 描述 : LCD 128*32 显示字符串
 * 作者  : www.keyes-robot.com
*/
#include "lcd128_32_io.h"

//创建 lCD128 *32 引脚，sda--->20， scl--->21
lcd lcd(20, 21);

void setup() {
  lcd.Init(); //初始化
  lcd.Clear();  //清除
}

void loop() {
  lcd.Cursor(0, 4); //设置显示位置
  lcd.Display("KEYESTUDIO"); //设置显示参数
  lcd.Cursor(1, 0);
  lcd.Display("ABCDEFGHIJKLMNOPQR");
  lcd.Cursor(2, 0);
  lcd.Display("123456789+-*/<>=$@");
  lcd.Cursor(3, 0);
  lcd.Display("%^&(){}:;'|?,.~\\[]");
}
```

项目现象：

项目代码上传成功后，利用USB线上电，你会看到的现象是：128X32LCD模块显示屏第一行显示“KEYESTUDIO”、第二行显示“ABCDEFGHIJKLMNOPQR”、第三行显示“123456789+-\*/\<\>=$@”、第四行显示“%^&(){}:;'|?,.~\\\[\]”。

### 项目 18：小风扇

项目介绍：

在炎热的夏季，需要电扇来给我们降温，那么在这个项目中，我们将使用树莓派Pico板控制130电机模块和小扇叶来制作一个小电扇。

项目元件：

|  ![](media/b18fe281156b29c44796f72222718d58.jpeg)  |  ![](media/2762753d227ba94de9f6e5c9ff79fe53.png)  |  ![](media/e615c2849ce15bdfc8e26432004124ec.png)  |
|-|-|-|
| 树莓派Pico板*1 | 树莓派Pico板的扩展板*1 | 4P 转杜邦线母单10厘米*1 |
|  ![](media/6d454cc922ceff4087d9ab1e5ccf030f.png)  |  ![](media/7dcbd02995be3c142b2f97df7f7c03ce.png)  |  ![](media/824d7ee32f701ea11eb91a9d1865d6ac.png)  |
| 130电机模块*1 | USB 线*1 | 三叶软桨*1 |

元件知识:

![](media/6d454cc922ceff4087d9ab1e5ccf030f.png)

130电机模块：该电机控制模块采用HR1124S电机控制芯片。HR1124S是应用于直流电机方案的单通道H桥驱动器芯片。HR1124S的H桥驱动部分采用低导通电阻的PMOS和NMOS功率管。低导通电阻保证芯片低的功率损耗，使得芯片安全工作更长时间。此外HR1124S拥有低待机电流，低静态工作电流，这些性能使HR1124S易用于玩具方案。

130电机模块参数：

工作电压：5V

工作电流：200MA

工作功率：2W

工作温度：-10℃~+50℃

130电机模块原理图：

![](media/ee2deb2ed7ae310b953ff178aff3d6c1.emf)

项目电路图和接线图：

![](media/790a5dafc169477f382bcee7a2ce82a5.png)

![](media/b5054883392228c86af0586520300b07.png)

项目代码：

```C
/*
 * 文件名 : 小风扇
 * 描述 : 风机顺时针旋转，停止，逆时针旋转，停止，循环.
 * 作者  : www.keyes-robot.com
*/
#define Motorla    17  // 定义电机的Motor_IN+引脚
#define Motorlb     16  // 定义电机的Motor_IN-引脚

void setup(){
  pinMode(Motorla, OUTPUT);//设置Motorla为OUTPUT
  pinMode(Motorlb, OUTPUT);//将Motorlb设置为OUTPUT
}
void loop(){
//设置逆时针旋转5秒
  digitalWrite(Motorla,HIGH);
  digitalWrite(Motorlb,LOW);
  delay(5000);
//设置停止旋转2秒 
  digitalWrite(Motorla,LOW);
  digitalWrite(Motorlb,LOW);
  delay(2000);
//设置顺时针旋转5秒
  digitalWrite(Motorla,LOW);
  digitalWrite(Motorlb,HIGH);
  delay(5000);
//设置停止旋转2秒 
  digitalWrite(Motorla,LOW);
  digitalWrite(Motorlb,LOW);
  delay(2000);
}
```

项目现象：

项目代码上传成功后，利用USB线上电，可以看到的现象是：小风扇先逆时针转5秒，停止2秒，再顺时针转5秒，停止2秒，以此规律重复执行。

### 项目 19：舵机

项目介绍：

舵机是一种可以非常精确地旋转的电机。目前已广泛应用于玩具车、遥控直升机、飞机、机器人等领域。在这个项目中，我们将使用树莓派Pico板控制舵机转动。

项目元件：

|  ![](media/b18fe281156b29c44796f72222718d58.jpeg)  |  ![](media/2762753d227ba94de9f6e5c9ff79fe53.png)  |
|-|-|
| 树莓派Pico板*1 | 树莓派Pico板的扩展板*1 |
|  ![](media/cd0bc424e9916881a1a903793821a042.png)  |  ![](media/7dcbd02995be3c142b2f97df7f7c03ce.png)  |
| 舵机*1 | USB 线*1 |

元件知识：

舵机：

![](media/99830768916233a9c5900ac399006c17.png)

舵机是一种位置伺服的驱动器，主要是由外壳、电路板、无核心马达、齿轮与位置检测器所构成。其工作原理是由接收机或者单片机发出信号给舵机，其内部有一个基准电路，产生周期为20ms，宽度为1.5ms的基准信号，将获得的直流偏置电压与电位器的电压比较，获得电压差输出。经由电路板上的IC判断转动方向，再驱动无核心马达开始转动，透过减速齿轮将动力传至摆臂，同时由位置检测器送回信号，判断是否已经到达定位。适用于那些需要角度不断变化并可以保持的控制系统。当电机转速一定时，通过级联减速齿轮带动电位器旋转，使得电压差为0，电机停止转动。一般舵机旋转的角度范围是0度到180
度。

控制舵机的脉冲周期为20ms，脉冲宽度为0.5ms ~ 2.5ms，对应位置为-90°~+90°。下面是以一个180°角的舵机为例：

![](media/708316fde05c62113a3024e0efb0c237.jpeg)

伺服电机有多种规格，但它们都有三根连接线，分别是棕色、红色、橙色(不同品牌可能有不同的颜色)。棕色为GND，红色为电源正极，橙色为信号线。

![](media/3f5bc31305e64108bed3b3619d602891.jpeg)

项目接线图：

舵机供电时请注意，电源电压应为3.3V-5V。请确保在将舵机连接到电源时不会出现任何错误。

![](media/4c7d70f701ec116ac4195faf3a9e545e.png)

添加Servo库：

本项目代码使用了一个名为“Servo”库，如果你还没有添加它，请在学习之前添加。添加第三方库的步骤如下:
    
方法一：
    
打开arduino     IDE，点击“项目”→“加载库”→“管理库...”。在搜索栏输入“Servo”，选择“Servo”并点击“安装”进行添加。请参考以下操作：

![](media/704a73e66330bf11890efd31cf64142f.png)

![](media/7ae50307ee02d48703546ea89b1e30a2.png)

方法二：

打开Arduino IDE，单击“项目”→“加载库”→“添加 .ZIP库...”。在弹出窗口中找到该目录下名为：4. Arduino 教程\1. Windows 系统\3.库文件\Servo.ZIP的文件。先单击“Servo.ZIP”文件，再单击“打开”。

![](media/49826d55f716443a0515bed016c6c758.png)

![](media/95776e1770d552aa5ef842a2a20a73bb.png)

项目代码：

Servo库添加完成后，你可以打开我们提供的代码：


```C
/*
 * 文件名 : 舵机
 * 描述 : 控制舵机进行清扫
 * 作者 : www.keyes-robot.com
*/
#include <Servo.h>
#define servoPin 16

Servo myServo;  // 创建舵机对象来控制舵机
int pos = 0;    // 创建变量存储舵机位置

void setup() {
  myServo.attach(servoPin);  // 将舵机连接到GP16引脚上
}

void loop() {
  for (pos = 0; pos <= 180; pos += 1) { // 从0°到180°
    // 以1度为阶
    myServo.write(pos);              // 告诉舵机在变量'pos'的位置
    delay(15);                       // 等待15毫秒舵机到达对应位置
  }
  for (pos = 180; pos >= 0; pos -= 1) { // 从180度到0度
    myServo.write(pos);              // 告诉舵机在变量'pos'的位置
    delay(15);                       // 等待15毫秒舵机到达对应位置
  }
}
```

项目现象：

项目代码上传成功后，利用USB线上电，你会看到的现象是：舵机将从0°旋转到180°，然后反转方向使其从180°旋转到0°，并在一个无限循环中重复这些动作。

![](media/c5250405a4290ecb2d758ff1097310c7.png)

### 项目 20：步进电机 

项目介绍：

步进电机定位准确，是工业机器人、3D打印机、大型车床等机械设备中最重要的部件。在这个项目中，我们将使用树莓派Pico板控制ULN2003步进电机驱动板来驱动步进电机转动。

项目元件：

|  ![](media/e4d763773ba5bc91a3df128e040f491e.jpeg)  |  ![](media/2762753d227ba94de9f6e5c9ff79fe53.png)  |  ![](media/52227712f82a477e2c6abfad08529e93.png)  |
|-|-|-|
| 树莓派Pico板*1 | 树莓派Pico板的扩展板*1 | ULN2003步进电机驱动板*1 |
|  ![](media/8ebb14a35091dc8d02d95cb6748dd1e9.png)  |  ![](media/df84a18afd8b37097c469ae3ac208bc4.png)  |  ![](media/7dcbd02995be3c142b2f97df7f7c03ce.png)  |
| 步进电机*1 | 母对母杜邦线若干 | USB 线*1 |

项目知识：

![](media/8ebb14a35091dc8d02d95cb6748dd1e9.png)

步进电机：是由一系列电磁线圈控制的电机。它可以根据需要旋转精确的度数(或步数)，允许你将它移动到一个精确的位置并保持该位置。它是通过在很短的时间内为电机内部的线圈供电来做到这一点的，但你必须一直为电机供电，以保持它在你想要的位置。有两种基本类型的步进电机，单极步进和双极步进。在本项目中，我们使用的是单极步进电机28-BYJ48。

![](media/78276dc113309152d542fc3f06a24757.png)

28BYJ-48步进电机工作原理：

步进电机主要由定子和转子组成，定子是固定不动的，如下图绕着A、B、C、D线圈组的部分，线圈组导通电就会产生磁场；转子就是转动的部分，如下图定子中间的部分，两极是永磁铁。

![](media/32748e0804b1fff434181cb228b23242.png)

单步4节拍的转动原理：开始A组线圈导通，转子两极正对着A组线圈；接着A组线圈断开，B组线圈导通，转子就会顺时针转到B组线圈，转子转了一步；B断开，C导通，转子转到C组；C断开，D导通，转子转到D组；D组断开，A组导通，转子转到A组线圈。这样转子就转了半圈180度，接着再重复一次，B-C-D-A，转子转回到A组线圈，这样转子就转了一圈，总共转动了8步。如下图所示，这就是步进电机单节拍转动的原理A -
B - C - D - A ....。

如果想让步进电机逆时针转动，那只要把节拍顺序反过来就行，D - C - B - A -
D .....。

![](media/b8ae50bbdee2dd5bc683e8c450baee6a.png)

半步8节拍转动原理：8节拍，采用的是单双拍的形式，A - AB - B - BC - C -
CD - D - DA - A ......
，这样运转一拍，转子只会转动半步，例如，A组线圈导通，转子转到正对着A组线圈；接着A和B组一起导通，这样产生的磁场最强的地方在AB组线圈中间，转子两极就会转到AB组线圈中间，也就是顺时针转了半步。

步进电机参数：

我们所提供的步进电机需要转动32步，转子才能转一圈，还经过了1:64的减速齿轮组带动输出轴，这样输出轴转动一圈需要：32 \* 64 = 2048 步。

电压5V，4相步进电机 ，4节拍模式的步进角为11.25，8节拍模式步进角为5.625， 减速比为1:64

ULN2003步进电机驱动板：ULN2003型步进电机驱动器，将微弱信号转换为更强的控制信号，从而驱动步进电机。

下面的原理图显示了如何使用ULN2003步进电机驱动板接口将一个单极步进电机接到Raspberry Pi Pico的引脚上，并显示了如何使用四个TIP120的接口。

![](media/6fa632d2b70e97dd55565d23ec15d245.png)

项目原理图和接线图：

![](media/81301873ba52d2319b71fc3e61ff7d5d.png)

![](media/b9751d6689231336b15e7993995da032.png)

项目代码：

```C
/*
 * 文件名 : 步进电机
 * 描述 : 使用ULN2003驱动步进电机.
 * 作者 : www.keyes-robot.com
*/
// 连接步进电机驱动器的端口
int outPorts[] = {21, 20, 19, 18};

void setup() {
  // set pins to output
  for (int i = 0; i < 4; i++) {
    pinMode(outPorts[i], OUTPUT);
  }
}

void loop()
{
  // 旋转一圈
  moveSteps(true, 32 * 64, 3);
  delay(1000);
  // 向另一个方向旋转一圈
  moveSteps(false, 32 * 64, 3);
  delay(1000);
}

//建议:在3 ~ 20毫秒范围内电机转动精确
void moveSteps(bool dir, int steps, byte ms) {
  for (unsigned long i = 0; i < steps; i++) {
    moveOneStep(dir); // 旋转一步
    delay(constrain(ms,3,20));        // 控制速度
  }
}

void moveOneStep(bool dir) {
  // 定义一个变量，用四位低位表示端口的状态
  static byte out = 0x01;
  // 根据旋转方向确定移动方向
  if (dir) {  // 循环左移
    out != 0x08 ? out = out << 1 : out = 0x01;
  }
  else {      // 循环右移
    out != 0x01 ? out = out >> 1 : out = 0x08;
  }
  // 向每个端口输出信号
  for (int i = 0; i < 4; i++) {
    digitalWrite(outPorts[i], (out & (0x01 << i)) ? HIGH : LOW);
  }
}

void moveAround(bool dir, int turns, byte ms){
  for(int i=0;i<turns;i++)
    moveSteps(dir,32*64,ms);
}
void moveAngle(bool dir, int angle, byte ms){
  moveSteps(dir,(angle*32*64/360),ms);
}
```

项目结果：

项目代码上传成功后，利用USB线上电，可以看到的现象是：ULN2003驱动模块上D1D2D3D4四个LED点亮，步进电机先顺时针旋转，再逆时针旋转，并保持此状态循环。

![](media/8dc4a0547390e0108c3960c31d330ee7.png)

### 项目 21：继电器

项目介绍：

在日常生活中，我们一般使用交流来驱动电气设备，有时我们会用开关来控制电器。如果将开关直接连接到交流电路上，一旦发生漏电，人就有危险。从安全的角度考虑，我们特别设计了这款具有NO(常开)端和NC(常闭)端的继电器模块。在这节课我们将学习一个比较特殊，好用的开关，就是继电器模块。

项目元件：

|  ![](media/5207588df3059cf385957664d41e9ac6.jpeg)  |  ![](media/2762753d227ba94de9f6e5c9ff79fe53.png)  |  ![](media/c141037e768eebd697ea07520708ee47.png)  |  ![](media/ebfacf0e05bcd3941fe3b641935756b0.png)  |  ![](media/7dcbd02995be3c142b2f97df7f7c03ce.png)  |
|-|-|-|-|-|
| 树莓派Pico板*1 | 树莓派Pico板的扩展板*1 | 继电器模块*1 | 3Pin 杜邦母单*1 | USB 线*1 |

元件知识：

继电器：是用小电流去控制大电流运作的一种“自动开关”。

输入电压：3.3V-5V

额定负载：5A 250VAC (NO/NC) 5A 24VDC (NO/NC)

额定负载的意思是，可以使用微型控制器的3.3V-5V控制直流电压24V或者交流电压250V的设备。

继电器原理图：

![](media/be1c90d2b52fc2489590e3f702a087bf.emf)

项目接线图：

![](media/5f8b3dfaee621c787083664297b7e094.png)

![](media/33bc9c0cb70b5b4edeb12b8326ab02a9.png)

项目代码：

```C
/*
 * 文件名 : 继电器
 * 描述 : 继电器打开和关闭.
 * 作者 : www.keyes-robot.com
*/
#define  Relay  16 // 定义了数字引脚16
void setup()
{
  pinMode(Relay, OUTPUT); // 数字 "Relay" 作为输出
}
void loop()
{
  digitalWrite(Relay, HIGH); // 打开继电器
  delay(1000); //延时1秒
  digitalWrite(Relay, LOW); // 关闭继电器
  delay(1000); // 延时1秒
}
```

项目结果：

项目代码上传成功后，利用USB线上电，可以看到的现象是：继电器将循环开与关，开启1秒，关闭1秒。同时可以听到继电器开与关的声音，还可以看到继电器上的指示灯指示状态的变化。

### 项目 22：调光灯

项目介绍：

电位器是一个带有滑动或旋转触点的三端电阻器，它形成一个可调的分压器。它的工作原理是在均匀电阻上改变滑动触点的位置。在电位器中，整个输入电压被施加到电阻的整个长度上，输出电压是固定触点和滑动触点之间的电压值。在这个项目中，我们将学习使用树莓派Pico板读取电位器的值，并结合LED制作一个调光灯。

项目元件：

|  ![](media/b1265f71184b5d144248ea3e847a18c9.jpeg)  |  ![](media/2762753d227ba94de9f6e5c9ff79fe53.png)  |  ![](media/03ab81e8b4f09287d2781ef0fd297f85.png)  |  ![](media/ef77f5a64c382157fc2dea21ec373fef.png)  |
|-|-|-|-|
| 树莓派Pico板*1 | 树莓派Pico板的扩展板*1 | 可调电位器*1 | 红色 LED*1 |
|  ![](media/e380dd26e4825be9a768973802a55fe6.png)  |  ![](media/845d05a6108b1662b828610ba9dcb788.png)  |  ![](media/3ac518b4caa5086041545c60c7a6a2d1.png)  |  ![](media/7dcbd02995be3c142b2f97df7f7c03ce.png)  |
| 面包板*1 | 220Ω电阻*1 | 公对母杜邦线若干 | USB 线*1 |

元件知识：

![](media/03ab81e8b4f09287d2781ef0fd297f85.png)

可调电位器：可调电位器是电阻和模拟电子元件的一种，具有0和1两种状态(高电平和低电平)。模拟值不同，其数据状态呈现为1
~ 1024等线性状态。

读取电位器的ADC值和电压值：

我们将电位器连接到树莓派Pico板的模拟IO口上来读取电位器的ADC值和电压值。接线请参照以下接线图：

![](media/fb36b9b05a5082fe9255b42eb90cf330.png)

```C
/*  
 * 文件名 : 读取电位器模拟值
 * 描述 : ADC的基本用法
 * 作者  : www.keyes-robot.com
*/
#define PIN_ANALOG_IN  26  //电位计的引脚

void setup() {
  Serial.begin(115200);
}

//在loop()函数中，调用analogRead来获取ADC0的ADC值，并将其赋给adcVal. 
//通过公式计算测量到的电压值，并通过串口监视器打印这些数据.
void loop() {
  int adcVal = analogRead(PIN_ANALOG_IN);
  double voltage = adcVal / 1023.0 * 3.3;
  Serial.println("ADC Value: " + String(adcVal) + " --- Voltage Value: " + String(voltage) + "V");
  delay(500);
}
```

上传代码成功之后，利用USB线上电，打开串口监视器，设置波特率为115200。可以看到的现象是：串口监视器窗口将打印出电位器的ADC值和电压值，转动电位器手柄时，ADC值和电压值发生变化。如下图所示：

![](media/0d9212c335a998d6783758f40f6095bc.png)

调光灯的电路图和接线图：

在前面一步，我们读取了电位器的ADC值和电压值，现在我们需要将电位器的ADC值转换成LED的亮度，来做成一个亮度可调的灯。见如下所示接线图：

![](media/f13f9ab55497276e4323ca97479cb69b.png)

![](media/b08506476d0321b6040e38ce2992f775.png)

项目代码：

```C
/*  
 * 文件名 : 调光灯
 * 描述 : 利用电位器控制LED的亮度.
 * 作者 : www.keyes-robot.com
*/
#define PIN_ADC0    26  //电位器的引脚
#define PIN_LED     16  // LED的引脚

void setup() {
  pinMode(PIN_LED, OUTPUT);
  pinMode(PIN_ADC0, INPUT);
}

void loop() {
  int adcVal = analogRead(PIN_ADC0); //读取电位器的ADC值
  analogWrite(PIN_LED, map(adcVal, 0, 1023, 0, 255));//将ADC映射到PWM占空比来控制LED亮度.
  delay(10);
}
```

项目现象：

下载项目代码到树莓派Pico板，通过转动电位器手柄改变GP26的输入电压，树莓派Pico板根据这个电压值改变GP16的输出电压，从而改变LED的亮度。

![](media/eca30dead3f4923afa0dcb0306db2319.jpeg)

### 项目 23：火焰警报器

项目介绍：

火灾是一种可怕的灾害，火灾报警系统在房屋，商业建筑和工厂中是非常有用的。在本项目中，我们将使用树莓派Pico板控制火焰传感器，蜂鸣器和LED来模拟火灾报警装置。这是一个有意义的创客活动。

项目元件：

|  ![](media/f70a6a892505b1816d151452b9b995a7.jpeg)  |  ![](media/2762753d227ba94de9f6e5c9ff79fe53.png)  |  ![](media/a50ec3e38adf10643eafac8cb62bec8a.png)  |  ![](media/ef77f5a64c382157fc2dea21ec373fef.png)  |  ![](media/4b4f653a76a82a3b413855493cc58fba.png)  |
|-|-|-|-|-|
| 树莓派Pico板*1 | 树莓派Pico板的扩展板*1 | 火焰传感器*1 | 红色 LED*1 | 有源蜂鸣器*1 |
|  ![](media/e380dd26e4825be9a768973802a55fe6.png)  |  ![](media/845d05a6108b1662b828610ba9dcb788.png)  |  ![](media/b395b1cd2678f87b3a34dec15659efbc.png)  |  ![](media/e9a8d050105397bb183512fb4ffdd2f6.png)  |  ![](media/7dcbd02995be3c142b2f97df7f7c03ce.png)  |
| 面包板*1 | 220Ω电阻*1 | 10KΩ电阻*1 | 跳线若干 | USB 线*1 |
|  ![](media/9197d4aff9356c585b7ef68e33a6881d.png)  |  ![](media/098a2730d0b0a2a4b2079e0fc87fd38b.png)  |  ![](media/3ac518b4caa5086041545c60c7a6a2d1.png)  |  |  |
| NPN型晶体管(S8050)*1 | 1kΩ电阻*1 | 公对母杜邦线若干 |  |  |

元件知识：

![](media/a50ec3e38adf10643eafac8cb62bec8a.png)

火焰会发出一定程度的IR光，这种光人眼是看不到的，但我们的火焰传感器可以检测到它，并提醒微控制器（如树莓派Pico板）已经检测到火灾。它有一个专门设计的红外接收管来探测火焰，然后将火焰亮度转换为波动水平信号。接收三极管的短引脚是负极，另一个长引脚是正极。我们应该连接短引脚（负极)到5V，连接长
引脚(正极)到模拟引脚，一个电阻和GND。如下图所示：

![](media/3f0435fdd13d8f1845a04014709d0f41.jpeg)

注意：火焰传感器应避开日光、汽车头灯、白炽灯直接照射，也不能对着热源(如暖气片、加热器)或空调，以避免环境温度较大的变化而造成误报。同时还易受射频辐射的干扰。

读取火焰传感器的ADC值和电压值：

我们首先用一个简单的代码来读取火焰传感器的模拟值，并将其打印出来。接线请参照以下接线图：

![](media/2b4e0cf420a0a94d6dfdd184f06fae33.png)

![](media/87ee33681081148ff7b4e8f6ed485833.png)

```C
/*  
 * 文件名  : 读取火焰传感器模拟值
 * 描述 : ADC的基本用法
 * 作者 : www.keyes-robot.com
*/
#define PIN_ANALOG_IN  26  //火焰传感器的引脚

void setup() {
  Serial.begin(115200);
}

//在loop()函数中，调用analogRead来获取ADC0的ADC值，并将其赋给adcVal. 
//通过公式计算测量到的电压值，并通过串口监视器打印这些数据.
void loop() {
  int adcVal = analogRead(PIN_ANALOG_IN);
  double voltage = adcVal / 1023.0 * 3.3;
  Serial.println("ADC Value: " + String(adcVal) + " --- Voltage Value: " + String(voltage) + "V");
  delay(500);
}
```

上传代码成功之后，利用USB线上电，打开串口监视器，设置波特率为115200。可以看到的现象是：串口监视器窗口将打印出火焰传感器读取的模拟值，当火焰靠近火焰传感器时，模拟值增大；反之，模拟值减小。

![](media/c9783eb837b702694dffa8562e93d178.png)

火焰报警的电路图和接线图：

接下来，我们将使用火焰传感器和蜂鸣器、LED制作一个有趣的项目——火灾报警装置。当火焰传感器检测到火焰时，LED闪烁，蜂鸣器报警。

![](media/2f4b0e24a35b7e5b90e7577bd4fa13b5.png)

![](media/897914ede2a0a221bff1da80d7ead221.png)

项目代码：

（注意：![](media/4b3a41657bb185bc081cc3768c117634.png)代码中的阀值500可以根据实际情况自己重新设置）

```C
/*  
 * 文件名  : 火焰报警器
 * 描述 : 通过火焰传感器控制蜂鸣器和LED.
 * 作者 : www.keyes-robot.com
*/
#define PIN_ADC0      26  //火焰传感器的引脚
#define PIN_LED       16  // LED的引脚
#define PIN_BUZZER    17  // 蜂鸣器的引脚

void setup() {
  pinMode(PIN_LED, OUTPUT);
  pinMode(PIN_BUZZER, OUTPUT);
  pinMode(PIN_ADC0, INPUT);
}

void loop() {
  int adcVal = analogRead(PIN_ADC0); //读取火焰传感器的ADC值
  if (adcVal >= 500) {
    digitalWrite (PIN_BUZZER, HIGH); //蜂鸣器鸣叫
    digitalWrite(PIN_LED, HIGH); // 点亮LED
    delay(500); // 延时0.5秒.
    digitalWrite(PIN_LED, LOW); // 熄灭 LED
    delay(500); // 延时0.5秒
  }
  else
  {
    digitalWrite(PIN_LED, LOW);  //熄灭LED
    digitalWrite (PIN_BUZZER, LOW); //关闭蜂鸣器
  }
}
```


项目现象：

项目代码上传成功后，利用USB线上电，可以看到的现象是：当火焰传感器检测到火焰时，LED闪烁，蜂鸣器报警；否则，LED不亮，蜂鸣器不响。

### 项目 24：小夜灯

项目介绍：

传感器或元件在我们的日常生活中是无处不在的。例如，一些公共路灯在晚上会自动亮起，而在白天会自动熄灭。为什么呢?
事实上，这些都是利用了一种光敏元件，可以感应外部环境光强度的元件。晚上，当室外亮度降低时，路灯会自动打开；到了白天，路灯会自动关闭。这其中的原理是很简单的，在本实验中我们使用树莓派Pico板控制LED就来实现这个路灯的效果。

项目元件：

|  ![](media/f70a6a892505b1816d151452b9b995a7.jpeg)  |  ![](media/2762753d227ba94de9f6e5c9ff79fe53.png)  |  ![](media/9e553e75b6f976f33438171eb2f2e775.png)  |  ![](media/ef77f5a64c382157fc2dea21ec373fef.png)  |  ![](media/b395b1cd2678f87b3a34dec15659efbc.png)  |
|-|-|-|-|-|
| 树莓派Pico板*1 | 树莓派Pico板的扩展板*1 | 光敏电阻*1 | 红色 LED*1 | 10KΩ电阻*1 |
|  ![](media/e380dd26e4825be9a768973802a55fe6.png)  |  ![](media/3ac518b4caa5086041545c60c7a6a2d1.png)  |  ![](media/e9a8d050105397bb183512fb4ffdd2f6.png)  |  ![](media/7dcbd02995be3c142b2f97df7f7c03ce.png)  |  ![](media/845d05a6108b1662b828610ba9dcb788.png)  |
| 面包板*1 | 公对母杜邦线若干 | 跳线若干 | USB 线*1 | 220Ω电阻*1 |

元件知识：

![](media/9e553e75b6f976f33438171eb2f2e775.png)

光敏电阻：是一种感光电阻，其原理是光敏电阻表面上接收亮度(光)降低电阻，光敏电阻的电阻值会随着被探测到的环境光的强弱而变化。有了这个特性，我们可以使用光敏电阻来检测光强。光敏电阻及其电子符号如下

![](media/7d575da675a2f6cb511d28b801e2abaa.png)

下面的电路是用来检测光敏电阻电阻值的变化：

![](media/5a7f7e641eb78007760a94151c1d80a5.png)

在上述电路中，当光敏电阻的电阻因光强的变化而改变时，光敏电阻与电阻R2之间的电压也会发生变化。因此，通过测量这个电压就可以得到光的强度。

读取光敏电阻的ADC值和电压值：

我们首先用一个简单的代码来读取光敏电阻模拟值，并将其打印出来。接线请参照以下接线图：

![](media/68337a2b734098f3bf0ad3fe5409dd2c.png)

![](media/f959367212742f632f48c79543e74c48.png)


```C
/*  
 * 文件名 : 读取光敏电阻的模拟值
 * 描述 : ADC的基本用法
 * 作者  : www.keyes-robot.com
*/
#define PIN_ANALOG_IN  26  //光敏传感器的引脚

void setup() {
  Serial.begin(115200);
}

//在loop()函数中，调用analogRead来获取ADC0的ADC值，并将其赋给adcVal. 
//通过公式计算测量到的电压值，并通过串口监视器打印这些数据.
void loop() {
  int adcVal = analogRead(PIN_ANALOG_IN);
  double voltage = adcVal / 1023.0 * 3.3;
  Serial.println("ADC Value: " + String(adcVal) + " --- Voltage Value: " + String(voltage) + "V");
  delay(500);
}
```

上传代码成功之后，利用USB线上电，打开串口监视器，设置波特率为115200。可以看到的现象是：串口监视器窗口将打印光敏电阻读取的模拟值，当逐渐减弱光敏电阻所处环境中的光线强度时，模拟值逐渐增大；反之，模拟值逐渐减小。

![](media/c9783eb837b702694dffa8562e93d178.png)

光控灯的电路图和接线图：

我们在前面做了一个小小的调光灯，现在我们来做一个光控灯。它们的原理是相同的，即通过树莓派Pico板获取传感器的模拟值，然后调节LED的亮度。

![](media/95040188210fa3c14fd3d61c4ecaac1e.png)

![](media/d34c5f9f70d90d560be98038169c270d.png)

项目代码：

```C
/*  
 * 文件名 : 小夜灯
 * 描述 : 光敏传感器控制LED的亮度.
 * 作者 : www.keyes-robot.com
*/
#define PIN_ADC0    26  // 光敏传感器的引脚
#define PIN_LED     16  // LED的引脚

void setup() {
  pinMode(PIN_LED, OUTPUT);
  pinMode(PIN_ADC0, INPUT);
}

void loop() {
  int adcVal = analogRead(PIN_ADC0); //读取光敏传感器的ADC值
  analogWrite(PIN_LED, map(adcVal, 0, 1023, 0, 255));//将ADC映射到PWM占空比来控制LED亮度.
  delay(10);
}
```

项目结果：

项目代码上传成功后，利用USB线上电，可以看到的现象是：当减弱光敏电阻所处环境中的光线强度时，LED变亮，反之，LED变暗。

### 项目 25：人体感应灯

项目介绍：

人体感应灯一般都用在黑漆漆的楼道区域，随着科技的发展，人体感应灯的使用在我们现实生活中十分常见。小区的楼道，房间的卧室、地下城的车库、卫生间等区域都会用到人体感应灯。现实生活中的人体感应灯一般是由人体红外传感器、灯、光敏电阻传感器等组成的。

在本项目中，我们将学习如何利用人体红外传感器、LED、光敏电阻来制作一款人体感应灯。

项目元件：

|  ![](media/f70a6a892505b1816d151452b9b995a7.jpeg)  |  ![](media/2762753d227ba94de9f6e5c9ff79fe53.png)  |  ![](media/82b6a0e286b6ca25c06c6353397bad79.png)  |  ![](media/7eb361d680dfa351f07f8527aeb37abd.png)  |
|-|-|-|-|-|
| 树莓派Pico板*1 | 树莓派Pico板的扩展板*1 | 光敏电阻*1 | 红色LED*1 |
|  ![](media/e380dd26e4825be9a768973802a55fe6.png)  |  ![](media/51ab4ab6eefe8ba8f66234989d5282de.png)  |  ![](media/7dcbd02995be3c142b2f97df7f7c03ce.png)  |  ![](media/e9a8d050105397bb183512fb4ffdd2f6.png)  |
| 面包板*1 | 220Ω电阻*1 | USB 线*1 | 跳线若干 |
|  ![](media/239131563f68fbf379e97d8384cf2d9b.png)  |  ![](media/ebfacf0e05bcd3941fe3b641935756b0.png)  |  ![](media/8cf9b1b3a5fec374cde3c5f0537567cb.png)  |  |
| 人体红外传感器*1 | 3Pin 杜邦母单*1 | 10KΩ电阻*1 |  |

项目电路图和接线图：

![](media/13089278eeceeda77e4023db7d60a29e.png)![](media/98a0ef97cf5053ad684f4197ea9f101b.png)

项目代码：

```C
/*  
 * 文件名 : 人体感应灯
 * 描述 : 采用光敏传感器和人体红外传感器控制LED.
 * 作者 : www.keyes-robot.com
*/
#define PIN_ADC0  26   //光敏传感器的引脚
#define PIN_LED1   16  // 外接LED的引脚
#define PIN_LED2   25  // Pico板上内置LED的引脚
#define pirPin   2     // 人体红外传感器的引脚
byte pirStat = 0;   // 人体红外传感器的状态
void setup() {
  pinMode(PIN_LED1, OUTPUT);
  pinMode(PIN_LED2, OUTPUT);
  pinMode(PIN_ADC0, INPUT);
  pinMode(pirPin, INPUT);
}

void loop() {
  int adcVal = analogRead(PIN_ADC0); //读取光敏传感器的ADC值
  pirStat = digitalRead(pirPin); //读取人体红外传感器的值
  if (adcVal >= 500) {
      digitalWrite(PIN_LED2, HIGH); //点亮Pico板上的内置LED 
      if (pirStat == HIGH){
         digitalWrite(PIN_LED1, HIGH);//点亮外接LED
         } 
      else{
         digitalWrite(PIN_LED1, LOW);//熄灭外接LED灯   
        }
  }
   else{
      digitalWrite(PIN_LED1, LOW);//熄灭外接LED灯
      digitalWrite(PIN_LED2, LOW);//熄灭Pico板上的内置LED
      }
}
```

项目结果：

项目代码上传成功后，利用USB线上电，可以看到的现象是：当你的手覆盖光敏电阻的感光部分来模拟黑暗状态时，树莓派Pico板内置的LED点亮，然后用另一只手在人体红外传感器前面晃动，外接LED也点亮，而且手停止晃动后过一会儿外接LED会自动关闭。如果光敏电阻的感光部分没有被覆盖，我们可以看到树莓派Pico板内置的LED灯熄灭了，这时候，用手在人体红外传感器前面晃动，外接LED处于熄灭状态。

### 项目 26：声控风扇

项目介绍：

声音传感器有一个内置的电容驻极体麦克风和功率放大器。它可以用来检测环境的声音强度。在这个项目中，我们利用树莓派Pico板控制声音传感器和电机模块模拟一个声控风扇。

项目元件：

|  ![](media/75f5433b4e64152b5cbd1857185eac5d.jpeg)  |  ![](media/2762753d227ba94de9f6e5c9ff79fe53.png)  |  ![](media/f716748aa248722612b2a78aade2cf5f.png)  |  ![](media/7dcbd02995be3c142b2f97df7f7c03ce.png)  |
|-|-|-|-|
| 树莓派Pico板*1 | 树莓派Pico板的扩展板*1 | 声音传感器*1 | USB 线*1 |
|  ![](media/46a9d1e772a02f86e12042d24ee04a30.png)  |  ![](media/ebfacf0e05bcd3941fe3b641935756b0.png)  |  ![](media/e615c2849ce15bdfc8e26432004124ec.png)  |  ![](media/16f725d5da5b576a79e9299717d811d7.png)  |
| 电机模块*1 | 3Pin 杜邦母单*1 | 4P 转杜邦线母单20厘米*1 | 三叶软桨*1 |

元件知识：

![](media/ff3135fc7fd316a52a6bca8fa8dd46ff.png)

声音传感器通常用于检测周围环境中的声音响度。微型控制板可以通过模拟输入接口采集其输出信号。S引脚是模拟输出，是麦克风电压信号的实时输出。传感器附带一个电位器，这样你就可以调整信号强度。你可以使用它来制作一些交互式作品，如语音操作的开关等。

读取声音传感器的模拟值：

我们首先使用一个简单的代码来读取声音传感器的模拟值，并将其打印出来，接线请参照以下接线图：

![](media/92b65cea0b7809a9e137f5993299d811.png)


```C
/*  
 * 文件名  : 读取声音传感器模拟值
 * 描述 : ADC的基本用法
 * 作者 : www.keyes-robot.com
*/
#define PIN_ANALOG_IN  28  //声音传感器的引脚

void setup() {
  Serial.begin(115200);
}

//在loop()函数中，调用analogRead来获取ADC0的ADC值，并将其赋给adcVal 
//通过公式计算测量到的电压值，并通过串口监视器打印这些数据.
void loop() {
  int adcVal = analogRead(PIN_ANALOG_IN);
  double voltage = adcVal / 1023.0 * 3.3;
  Serial.println("ADC Value: " + String(adcVal) + " --- Voltage Value: " + String(voltage) + "V");
  delay(500);
}
```

上传代码成功之后，利用USB线上电，打开串口监视器，设置波特率为115200。可以看到的现象是：串口监视器窗口将打印声音传感器读取的模拟值，当对着传感器拍拍手，声音传感器的模拟值发生了显著的变化。

![](media/c4f75573741c2fcf7e1e084940afd315.png)

智能风扇的接线图：

接下来，我们正式进入这个项目。我们用声音传感器、130电机模块和风叶片来模拟一个声控风扇。接线图如下：

![](media/5cdd94cfe38a87affede81ffa4c69b81.png)

![](media/86bf1beb216e6b62776921158a540618.png)

（注意：![](media/eadca6bc4da3706e43015b3e00afd512.png)代码中的阀值600可以根据实际情况自己重新设置）

项目代码：

```C
/*  
 * 文件名 : 声控风扇
 * 描述 : 通过声音传感器控制风扇.
 * 作者 : www.keyes-robot.com
*/
#define PIN_ADC2   28  //声音传感器的引脚
#define PIN_Motorla    17  // 电机的Motor_IN+引脚
#define PIN_Motorlb    16  // 电机的Motor_IN-引脚
#define PIN_LED    25  // // Pico板上内置LED的引脚

void setup() {
  pinMode(PIN_LED, OUTPUT);//设置PIN_LED为输出
  pinMode(PIN_Motorla, OUTPUT);//设置Motorla为输出
  pinMode(PIN_Motorlb, OUTPUT);//将Motorlb设置为输出
  pinMode(PIN_ADC2, INPUT);//设置PIN_ADC2为输入
}

void loop() {
  int adcVal = analogRead(PIN_ADC2); //读取声音传感器的ADC值
  if (adcVal > 600) {
    digitalWrite(PIN_LED,HIGH); //点亮Pico板上的内置LED
    digitalWrite(PIN_Motorla,HIGH); //旋转
    digitalWrite(PIN_Motorlb,LOW);
    delay(5000); //delay 5S
  }
  else
  {
    digitalWrite(PIN_LED,LOW); //关闭Pico板上的内置LED
    digitalWrite(PIN_Motorla,LOW); //停止转动
    digitalWrite(PIN_Motorlb,LOW); 
  }
}
```

项目现象：

项目代码上传成功后，利用USB线上电，可以看到的现象是：对着声音传感器拍拍手，当声音强度超过阈值时，小风扇旋转起来，同时树莓派Pico板内置的LED点亮；反之，小风扇不旋转，树莓派Pico板内置的LED不亮。

### 项目 27：温度测量

项目介绍：

LM35是一种常用且易于使用的温度传感器。它不需要其他硬件你只需要一个模拟端口就可以了。难点在于编译代码，将其读取的模拟值转换为摄氏温度。在这个项目中，我们使用树莓派Pico板控制一个温度传感器和3个LED灯来制作一个温度测试仪。当温度传感器接触不同的温度物体时，LED灯就会显示不同的颜色。

项目元件：

|  ![](media/74a834bb65d3f86d643648f2fa26430f.jpeg)  |  ![](media/2762753d227ba94de9f6e5c9ff79fe53.png)  |  ![](media/d66d216f88da9cfc289fb7ad10c959d6.png)  |  ![](media/7dcbd02995be3c142b2f97df7f7c03ce.png)  |
|-|-|-|-|
| 树莓派Pico板*1 | 树莓派Pico板的扩展板*1 | LM35温度传感器*1 | USB 线*1 |
|  ![](media/098a2730d0b0a2a4b2079e0fc87fd38b.png)  |  ![](media/0c1b0f91b4e56bcbc235d06b48809ac9.png)  |  ![](media/3ac518b4caa5086041545c60c7a6a2d1.png)  |  ![](media/e65c16153d0ca27891c8c08092d96d5a.png)  |
| 220Ω电阻*3 | 黄色 LED*1 | 公对母杜邦线若干 | 面包板*1 |
|  ![](media/afa6edd3ff90b027a6f43995a6fb15a2.png)  |  ![](media/6c688493b558ed5f3e90e7dab38cbd93.png)  |  ![](media/ebfacf0e05bcd3941fe3b641935756b0.png)  |  ![](media/e9a8d050105397bb183512fb4ffdd2f6.png)  |
| 红色LED*1 | 绿色 LED*1 | 3Pin 杜邦母单*1 | 跳线若干 |

元件知识：

![](media/d66d216f88da9cfc289fb7ad10c959d6.png)

LM35温度传感器工作原理：LM35是一种应用广泛的温度传感器，具有多种封装类型。在室温下，无需额外的校准处理即可达到1/4 C的精度。LM35温度传感器可以根据不同的温度产生不同的电压，当温度为0时输出0V;如果增加1，输出电压将增加10mv。输出温度为0
~100，换算公式如下：

![](media/0dfa07fa69f2a98658a3822c2da93bf7.jpeg)

读取LM35的温度值：

我们首先使用一个简单的代码读取温度传感器的值，并将其打印出来，其接线图如下所示：

![](media/8893887125785e6ac2e4be4af091f557.png)

这里，LM35输出给树莓派Pico板的模拟引脚是GP26。这个模拟电压被转换成数字形式，并经过处理得到温度读数。


```C
/*  
 * 文件名 : 读取LM35温度值
 * 描述 : 将ADC值转换为LM35温度值
 * 作者 : www.keyes-robot.com
*/
#define PIN_ANALOG_IN  26  //温度传感器的引脚

void setup() {
  Serial.begin(115200);
}

//在loop()函数中，调用analogRead来获取ADC0的ADC值，并将其赋给adcVal. 
//通过公式计算测量的电压值，摄氏度和华氏温度值，并通过串口监视器打印这些数据.
void loop() {
  int adcVal = analogRead(PIN_ANALOG_IN);
  double voltage = adcVal / 1023.0 * 3.3;
  float temperatureC = (voltage * 1000.0) / 10.0 ;
  float temperatureF = (temperatureC * 1.8) + 32.0;
  Serial.print("ADC Value: " + String(adcVal));
  Serial.print("---Voltage Value: " + String(voltage) + "V");
  Serial.print("---temperatureC: " + String(temperatureC) + "℃");
  Serial.println("---temperatureF: " + String(temperatureF) + "F");
  delay(500);
}
```

代码上传到树莓派Pico板成功之后，利用USB线上电，打开串口监视器，设置波特率为115200。可以看到的现象是：串口监视器窗口将打印当前环境的温度值。

![](media/5876ba72e4256ba9a7c458e7f592a6db.png)

温度测量的电路图和接线图：

 现在使用LM35温度传感器和3个led做一个温度测量。当LM35温度传感器感应到不同的温度时，不同的led会点亮。按照下图进行接线。
    
![](media/0d5cf74e28bc6d730c4571e8189f9372.png)

![](media/cbc79ab50cd4956254b7d3789804d6f2.png)

项目代码：

```C
/*  
 * 文件名  : 温度测量
 * 描述 : 当LM35感应到不同的温度时，不同的led会发光
 * 作者  : www.keyes-robot.com
*/
#define PIN_ADC2       26      //LM35传感器的引脚
#define PIN_GREENLED   22      // 绿色led的引脚
#define PIN_YELLOWLED  21      //黄色led的引脚
#define PIN_REDLED     19      //红色led的引脚
void setup() {
  Serial.begin(115200);
  pinMode(PIN_GREENLED, OUTPUT); //设置PIN_GREENLED为输出
  pinMode(PIN_YELLOWLED, OUTPUT);//设置pin_yellow为输出
  pinMode(PIN_REDLED, OUTPUT);//设置PIN_REDLED为输出
  pinMode(PIN_ADC2, INPUT);//设置PIN_ADC2为输入
}

void loop() {
  int adcVal = analogRead(PIN_ADC2);
  double voltage = adcVal / 1023.0 * 3.3;
  float temperatureC = (voltage * 1000.0) / 10.0 ;
  float temperatureF = (temperatureC * 1.8) + 32.0;
  Serial.print("ADC Value: " + String(adcVal));
  Serial.print("---Voltage Value: " + String(voltage) + "V");
  Serial.print("---temperatureC: " + String(temperatureC) + "℃");
  Serial.println("---temperatureF: " + String(temperatureF) + "F");
  if (temperatureF >= 95) {
    digitalWrite(PIN_GREENLED, LOW);
    digitalWrite(PIN_YELLOWLED, LOW);
    digitalWrite(PIN_REDLED, HIGH);
  }
  else if (temperatureF >= 90 && temperatureF < 95) {
    digitalWrite(PIN_GREENLED, LOW);
    digitalWrite(PIN_YELLOWLED, HIGH);
    digitalWrite(PIN_REDLED, LOW);
  }
  else {
    digitalWrite(PIN_GREENLED, HIGH);
    digitalWrite(PIN_YELLOWLED, LOW);
    digitalWrite(PIN_REDLED, LOW);
  }
  delay(500);
}
```

项目结果：

项目代码上传成功后，利用USB线上电，可以看到的现象是：当LM35温度传感器感应到不同的温度时，不同的led会点亮.

### 项目 28：摇杆控制RGB灯

项目介绍：

摇杆模块是一个有两个模拟输入和一个数字输入的组件。广泛应用于游戏操作、机器人控制、无人机控制等领域。在这个项目中，我们使用树莓派Pico板和摇杆模块控制RGB。这样，你可以在实践中对摇杆模块的原理和操作有更深入的了解。 

项目元件：

|  ![](media/b18fe281156b29c44796f72222718d58.jpeg)  |  ![](media/2762753d227ba94de9f6e5c9ff79fe53.png)  |  ![](media/43acc7d0046b997a4822d8fdab834c55.png)  |
|-|-|-|
| 树莓派Pico板*1 | 树莓派Pico板的扩展板*1 | 摇杆模块*1 |
|  ![](media/af749ecbde89c728a8c63e6527781cac.png)  |  ![](media/df55621713887ca0958d5fc0cceeb4ea.png)  |  ![](media/f1aed48e2c02214415853ad2358f3744.png)  |
| RGB LED*1 | 5P 转杜邦母单*1 | 公对母杜邦线若干 |
|  ![](media/098a2730d0b0a2a4b2079e0fc87fd38b.png)  |  ![](media/7dcbd02995be3c142b2f97df7f7c03ce.png)  |  ![](media/e65c16153d0ca27891c8c08092d96d5a.png)  |
| 220Ω电阻*3 | USB 线*1 | 面包板*1 |

元件知识：

![](media/43acc7d0046b997a4822d8fdab834c55.png)

摇杆模块：主要是采用PS2手柄摇杆元件，实际上摇杆模块有3个信号端引脚，模拟3维空间，摇杆模块的引脚分别是GND、VCC、信号端（B、X、Y），其中信号端X、Y模拟空间的X轴和Y轴，控制时，模块的X、Y信号端是连接单片机模拟口，通过控制2个模拟输入值来控制物体在空间X、Y轴的坐标。信号端B模拟空间Z轴，它一般是接数字口，做按键使用。

VCC接单片机电源输出端V/VCC（3.3/5V），GND接单片机G/GND，原始状态下读出电压大约为1.65V/2.5V左右，对于X轴方向，当随箭头方向逐渐按下，读出电压值随着增加，且可以达到最大电压，随箭头相反方向逐渐按下，读出电压值逐渐减少，减少到最小电压；对于Y轴方向，当沿着模块上的箭头方向逐渐按下，读出电压值逐渐减少，减少到最小电压，随箭头相反方向逐渐按下，读出电压值随着增加，且可以达到最大电压；对于Z轴方向，信号端B接数字口，原始状态下输出0，按下输出1。这样，我们可以读取两个模拟值和一个数字口的高低电平情况，判断模块上摇杆的工作状态。

模块参数：

输入电压：DC 3.3V ~ 5V

输出信号：X/Y双轴模拟值+Z轴数字信号

适用范围：适用于控制点坐标在平面内的运动，双自由度舵机的控制等。

产品特点：外观精美，摇杆手感优越，操作简单，反应灵敏，使用寿命长。

读取摇杆模块的值：

我们必须使用树莓派Pico板的模拟IO口从摇杆模块的X/Y引脚读取数据，并使用数字IO口读取按钮。请按照下面的接线图进行接线：

![](media/80c2deff4678a08725e0df9c3465db91.png)

![](media/bbcd8beb3a10e0f5d73dbd527b9dc0f1.png)

```C
/*  
 * 文件名 : 读取摇杆的值
 * 描述 : 从摇杆传感器读取数据.
 * 作者 : www.keyes-robot.com
*/
int xyzPins[] = {27, 26, 28};   //x, y, z引脚
void setup() {
  Serial.begin(115200);
  pinMode(xyzPins[0], INPUT); //x 轴. 
  pinMode(xyzPins[1], INPUT); //y 轴. 
  pinMode(xyzPins[2], INPUT_PULLUP);   //z 轴是一个按钮.
}

// 在loop()中，使用analogRead()读取X轴和Y轴的值，使用digitalRead()读取Z轴的值，然后显示它们.
void loop() {
  int xVal = analogRead(xyzPins[0]);
  int yVal = analogRead(xyzPins[1]);
  int zVal = digitalRead(xyzPins[2]);
  Serial.println("X,Y,Z: " + String(xVal) + ", " +  String(yVal) + ", " + String(zVal));
  delay(500);
}
```

代码上传到树莓派Pico板成功之后，利用USB线上电，打开串口监视器，设置波特率为115200。可以看到的现象是：串口监视器窗口将打印当前摇杆的模拟值和数字值，移动摇杆或按下它将改变串口监视器窗口中的模拟值和数字值。

![](media/fc2b5c16e690a43bb7a5edf08483e614.png)

![](media/3cbe057d90d72db79ba8ed29316ddca4.png)

摇杆模块控制RGB的电路图和接线图：

我们刚读了摇杆模块的值，这里我们需要用摇杆模块和RGB做一些事情，按照下图连接：

![](media/7e37e804327c10abce60756f6bb5cb3a.png)

![](media/4924a62ef1a0b35f3d6637a461929f03.png)

项目代码：

```C
/*  
 * 文件名 : 摇杆控制RGB
 * 描述 : 摇杆控制RGB亮不同颜色的光.
 * 作者 : www.keyes-robot.com
*/
int xyzPins[] = {27, 26, 28};   //x,y,z 引脚
int ledPins[] = {18, 17, 16};    //定义红，绿，蓝led引脚
void setup() {
  pinMode(xyzPins[0], INPUT); //x 轴. 
  pinMode(xyzPins[1], INPUT); //y 轴. 
  pinMode(xyzPins[2], INPUT_PULLUP);   //z 轴是一个按钮.
  for (int i = 0; i < 3; i++) {   //设置pwm通道,1KHz,8bit
    pinMode(ledPins[i], OUTPUT);
  }
}

// 在loop()中，使用analogRead()读取X轴和Y轴的值，使用digitalRead()读取Z轴的值，然后显示它们.
void loop() {
  int xVal = analogRead(xyzPins[0]);
  int yVal = analogRead(xyzPins[1]);
  int zVal = digitalRead(xyzPins[2]);
  if (xVal < 200){
     analogWrite(ledPins[0], 255); //共阴极LED，高电平点亮LED.
     analogWrite(ledPins[1], 0);
     analogWrite(ledPins[2], 0);
   }
  else if (xVal > 800){
     analogWrite(ledPins[0], 0); 
     analogWrite(ledPins[1], 255);
     analogWrite(ledPins[2], 0);
   }
  else if (yVal < 200){
     analogWrite(ledPins[0], 0); 
     analogWrite(ledPins[1], 0);
     analogWrite(ledPins[2], 255);
   }
  else if (yVal > 800){
     analogWrite(ledPins[0], 255); 
     analogWrite(ledPins[1], 255);
     analogWrite(ledPins[2], 255);
   }
}
```

项目现象：

项目代码上传成功后，利用USB线上电，可以看到的现象是：①如果摇杆在X方向上移动到最左边，RGB光变成红色;②如果摇杆在X方向上移动到最右边，RGB光变为绿色;③如果摇杆在Y方向上移动到最上面，RGB光变成白色;④如果摇杆在Y方向上移动到最下面，RGB光变成蓝色。

### 项目 29：温湿度表

项目介绍：

在冬季时，空气中的湿度很低，就是空气很干燥，再加上寒冷，人体的皮肤就容易过于干燥而裂，所以需要在用加湿器给家里的空气增加湿度，但是怎么知道空气过于干燥了呢？那就需要检测空气湿度的设备，这节课就来学习温湿度传感器的使用。我们使用温湿度传感器制作一个温湿度计，并且还结合LCD 128X32 DOT来显示温度和湿度值。

项目元件：

|  ![](media/20244a3eedd455abdbe6dc69b0ca6b8f.jpeg)  |  ![](media/2762753d227ba94de9f6e5c9ff79fe53.png)  |  ![](media/2d013e7634507fa3570235454abcd3fc.png)  |  ![](media/09f2e623faf741f16ba9b0390d587567.png)  |
|-|-|-|-|
| 树莓派Pico板*1 | 树莓派Pico板的扩展板*1 | 温湿度传感器*1 | LCD 128X32 DOT*1 |
|  ![](media/e615c2849ce15bdfc8e26432004124ec.png)  |  ![](media/ebfacf0e05bcd3941fe3b641935756b0.png)  |  ![](media/7dcbd02995be3c142b2f97df7f7c03ce.png)  |  |
| 4P 转杜邦线母单10厘米*1 | 3Pin 杜邦母单*1 | USB 线*1 |  |

元件知识：

![](media/2d013e7634507fa3570235454abcd3fc.png)

温湿度传感器：是一款含有已校准数字信号输出的温湿度复合传感器，其精度湿度±5%RH，
温度±2℃，量程湿度20-90%RH，
温度0~50℃。温湿度传感器应用专用的数字模块采集技术和温湿度传感技术，确保产品具有极高的可靠性和卓越的长期稳定性。温湿度传感器包括一个电阻式感湿元件和一个NTC测温元件，非常适用于对精度和实时性要求不高的温湿度测量场合。

工作电压在3.3V-5.5V范围内。  

温湿度传感器有三个引脚，分别为VCC，GND和S。S为数据输出的引脚。使用的是串行通讯。

温湿度传感器的单总线格式定义：

| 名称 | 单总线格式定义 |
|-|-|
| 起始信号 | 微处理器把数据总线(SDA)拉低一段时间至少 18ms(最大不得超过 30ms)，通知传感器准备数据。 |
| 响应信号 | 传感器把数据总线（SDA）拉低 83µs，再接高 87µs 以响应主机的起始信号。 |
| 湿度 | 湿度高位为湿度整数部分数据，湿度低位为湿度小数部分数据 |
| 温度 | 温度高位为温度整数部分数据，温度低位为温度小数部分数据，且温度低位 Bit8 为 1 则表示负温度，否则为正温度 |
| 校验位 | 校验位＝湿度高位+湿度低位+温度高位+温度低位 |

温湿度传感器数据时序图：

用户主机（MCU）发送一次开始信号后，温湿度传感器从低功耗模式转换到高速模式，待主机开始信号结束

后，温湿度传感器发送响应信号，送出 40bit
的数据，并触发一次信采集。信号发送如图所示。

![](media/c3038afcfc88d77da5ce9e8facf8ef32.png)

结合代码，可以理解的更好。

温湿度传感器可以很容易地将温湿度数据添加到您的DIY电子项目中。它是完美的远程气象站，家庭环境控制系统，和农场或花园监测系统。

温湿度传感器的参数：

工作电压：+5 V

温度范围：0-50 °C ，误差：± 2 °C

湿度范围：20-90% RH ，误差：± 5% RH

数字接口

温湿度传感器的原理图：

![](media/53fa034cbbcec22579b2bdf8252c90cd.emf)

读取温湿度值：

![](media/b2e37efa629abda91234abb91f32f7ee.png)

![](media/a202b07be0cd714ca3248b71191ba176.png)

怎样添加DHT库：

打开Arduino IDE，单击“项目”→“加载库”→“添加 .ZIP库...”。在弹出窗口中找到该目录下名为：4. Arduino 教程\1. Windows 系统\3.库文件\DHT.ZIP的文件。先单击“DHT.ZIP”文件，再单击“打开”。

![](media/dc921422ddfcaeb374113e60cf41054a.png)

![](media/a8559618c04aac0190f02b9e8af696d6.png)

DHT库添加完之后，你可以打开我们提供的代码：


```C
/*
 * 文件名 : 温湿度传感器
 * 描述 : 使用温湿度传感器测量温度和湿度。将结果打印到串口.
 * 作者 : www.keyes-robot.com
*/
//在使用dht11之前，我们需要包含一个头文件. 
//申请一个DHT对象，定义控制DHT的引脚为GPIO22.
#include <dht.h>

dht DHT;

#define DHT11_PIN 22

void setup(){
  Serial.begin(115200);
  delay(2000);
  Serial.println("Type,\tstatus,\tHumidity (%),\tTemperature (C)");
}

void loop(){
  int chk = DHT.read11(DHT11_PIN);//Read11()用于读取DHT11数据，并将返回值赋给变量chk.
//如果read11()函数的返回值不等于DHTLIB_OK，这意味着数据读取失败; 
//如果两者相等，则调用humuduty()和temperature()，获取当前环境的温湿度数据，并通过串口打印出来. 
  if(chk == DHTLIB_OK){
    Serial.println("humidity: " + String(DHT.humidity) + "%, temperature: " + String(DHT.temperature) + "C");
  }else{
    Serial.println("DHT11 Reading data error!");
  }
  delay(1000);
}
```

代码上传到树莓派Pico板成功之后，利用USB线上电，打开串口监视器，设置波特率为115200。可以看到的现象是：串口监视器窗口将打印当前显示当前环境中的温湿度数据，如下图。

![](media/ba7685fe5c2cf3baca5316d430ae6679.png)

温湿度仪表的电路图和接线图：

现在我们开始用LCD_128X32_DOT打印温湿度传感器的值，我们会在LCD_128X32_DOT的屏幕上看到相应的值。让我们开始这个项目吧。请按照下面的接线图进行接线：

特别注意：这里必须使用4P转杜邦线母单10厘米连接LCD_128X32_DOT，LCD_128X32_DOT才会显示正常；否则，使用4P转杜邦线母单20厘米，LCD_128X32_DOT可能会显示不正常。

![](media/beb75f9b8c582b57cb5846535f0575a2.png)

![](media/018d4ef8b56b679b71d1f2af5a907d84.png)

项目代码：

前面已经添加过DHT和lcd128_32_io库，可以不用重复添加。如果没有添加，就需要添加DHT和lcd128_32_io库，添加DHT库的方法请参照本项目的上面方法，添加lcd128_32_io库的方法请参照项目17：I2C 128×32 LCD中的方法。

DHT和lcd128_32_io库添加完成后，你可以打开我们提供的代码：

```C
/*
 * 文件名 : 温湿度仪表
 * 描述 : LCD显示温度值和湿度值.
 * 作者  : www.keyes-robot.com
*/
//在使用dht11之前，我们需要包含一个头文件. 
//申请一个DHT对象，定义控制DHT的引脚为GPIO22.
#include <dht.h>
dht DHT;
#define DHT11_PIN 22
//LCD128*32的库文件和lCD128*32的引脚
#include "lcd128_32_io.h"
lcd lcd(20, 21); //创建lCD128*32引脚，sda->20， scl->21

void setup(){
  lcd.Init(); //初始化
  lcd.Clear();  //清除
}
char string[10];

//LCD显示湿度值和温度值
void loop(){
  int chk = DHT.read11(DHT11_PIN);//Read11()用于读取DHT11数据，并将返回值赋给变量chk.
  lcd.Cursor(0,0); //设置显示位置
  lcd.Display("Temper:"); //设置显示参数
  lcd.Cursor(0,8);
  lcd.DisplayNum(DHT.temperature);
  lcd.Cursor(0,11);
  lcd.Display("C");
  lcd.Cursor(2,0); 
  lcd.Display("humid:");
  lcd.Cursor(2,8);
  lcd.DisplayNum(DHT.humidity);
  lcd.Cursor(2,11);
  lcd.Display("%");
  delay(200);
}
```

项目现象：

项目代码上传成功后，利用USB线上电，你会看到的现象是：LCD 128X32 DOT的屏幕上显示温湿度传感器检测环境中相应的温度值和湿度值。

### 项目 30：测距仪表

项目介绍：        HC-SR04超声波传感器是一种非常实惠的距离传感器，主要用于各种机器人项目中的物体躲避。它也被用于水位传感，甚至作为一个停车传感器。我们把超声波传感器当作蝙蝠的眼睛，在黑暗中，蝙蝠仍然可以通过超声波识别前方的物体和方向。在本项目中，我们使用树莓派Pico板控制超声波传感器和LED模拟超声波测距仪。

项目元件：

|  ![](media/ea74681ffd2116a2434692d34c25e829.jpeg)  |  ![](media/2762753d227ba94de9f6e5c9ff79fe53.png)  |  ![](media/11163a0fe0cd3efaf4ccd57fb237f103.png)  |  ![](media/3ac518b4caa5086041545c60c7a6a2d1.png)  |
|-|-|-|-|
| 树莓派Pico板*1 | 树莓派Pico板的扩展板*1 | 超声波传感器*1 | 公对母杜邦线若干 |
|  ![](media/e615c2849ce15bdfc8e26432004124ec.png)  |  ![](media/098a2730d0b0a2a4b2079e0fc87fd38b.png)  |  ![](media/e9a8d050105397bb183512fb4ffdd2f6.png)  |  ![](media/7eb361d680dfa351f07f8527aeb37abd.png)  |
| 4P 转杜邦线母单20厘米*1 | 220Ω电阻*4 | 跳线若干 | 红色LED*4 |
|  ![](media/e380dd26e4825be9a768973802a55fe6.png)  |  ![](media/7dcbd02995be3c142b2f97df7f7c03ce.png)  |  |  |
| 面包板*1 | USB 线*1 |  |  |

元件知识：

HC-SR04超声波传感器：像蝙蝠一样使用声纳来确定与物体的距离，它提供了精准的非接触范围检测，高精度和稳定的读数。它的操作不受阳光或黑色材料的影响，就像精密的照相机(在声学上像布料这样比较软的材料很难被探测到)。它带有超声波发射器和接收器。

![](media/653a2e40ce31d039c801edce1050114d.png)

在超声波传感器的前面是两个金属圆筒，这些是转换器。转换器将机械能转换成电信号。在超声波传感器中，有发射转换器和接收转换器。发射转换器将电信号转换为超声波脉冲，接收转换器将反射的超声波脉冲转换回电信号。如果你看超声波传感器的背面，你会看到的发射转换器后面有一个IC。这是控制发射转换器的IC。在接收转换器后面也有一个IC，这是一个四运算放大器，它将接收转换器产生的信号放大成足以传输到微控制器的信号。

时序图：

图示HC-SR04的时序图。为了开始测量，SR04的Trig必须接受至少10us的高(5V)脉冲，这将启动传感器将发射出8个周期的40kHz的超声波脉冲，并等待反射的超声波脉冲。当传感器从接收器检测到超声波时，它将设置回波引脚为高(5V)和延迟一个周期(宽度)，与距离成比例。为了获得距离，测量Echo引脚的宽度。

![](media/ba43be6007d9fe3aab0bb609868af640.png)

时间=回波脉冲宽度，单位为us(微秒)

距离厘米=时间/ 58

距离(英寸)=时间/ 148

读取超声波传感器距离值

我们将从一个简单的超声波测距开始，并打印测量的距离。
    
![](media/8835e046cfb2a4b1d6da5e78c473ccce.png)
    
HC-SR04超声波传感器有四个引脚：Vcc、Trig、Echo和GND。Vcc引脚提供产生超声波脉冲的电源，接Vcc/+5V。GND引脚接地/GND。Trig引脚是控制板发送信号来启动超声波脉冲的地方。Echo引脚是超声波传感器向控制板发送关于超声波脉冲行程持续时间的信息的地方。按下图接线：
    
 ![](media/9b58d226f676712f774b55978a89938c.png)

![](media/477cc941c0c2ad711a6b326f441c49e9.png)


```C
/*  
 * 文件名 : 测距仪表
 * 描述 : 使用超声波模块测量距离.
 * 作者 : www.keyes-robot.com
*/
const int TrigPin = 27; // 定义TrigPin
const int EchoPin = 26; // 定义EchoPin.
int duration = 0; // 将持续时间的初始值定义为0
int distance = 0;//定义距离的初始值为0
void setup() 
{
  pinMode(TrigPin , OUTPUT); // 设置trigPin为输出模式
  pinMode(EchoPin , INPUT); // 设置echoPin为输入模式
  Serial.begin(115200);  // 设置波特率为115200.
}
void loop()
{
 // 使trigPin高电平输出持续10μs触发HC_SR04 
  digitalWrite(TrigPin , HIGH);
  delayMicroseconds(10);
  digitalWrite(TrigPin , LOW);
  // 等待HC-SR04回到高电平，测量这个等待时间
  duration = pulseIn(EchoPin , HIGH);
  // 根据时间计算距离
  distance = (duration/2) / 28.5 ;
  Serial.print("Distance: ");
  Serial.print(distance); //串口打印距离值
  Serial.println("cm");
  delay(100); //等待100ms之间的pings(大约20pings /秒).
}
```

上传代码成功之后，利用USB线上电，打开串口监视器，设置波特率为115200。可以看到的现象是：串口监视器窗口将打印超声波传感器和物体之间的距离值。

![](media/8e9a99c61434bf735b6e34d92d60d69f.png)

超声波测距仪的电路图和接线图：

接下来，我们将使用树莓派Pico板控制超声波传感器和4个LED模拟超声波测距仪。按如下图接好线：
    
![](media/bf201b7c14c97e201dadcb24c687d3d5.png)

![](media/6596aa78b3aceb528058e22b3a26ed52.png)

项目代码：

```C
/*  
 * 文件名 : 测距仪表
 * 描述 : 四个led由超声波测距控制.
 * 作者 : www.keyes-robot.com
*/
const int TrigPin = 27;    // 定义TrigPin
const int EchoPin = 26;    // 定义EchoPin.
const int PIN_LED1 = 19;    // 定义PIN_LED1
const int PIN_LED2 = 18;    // 定义PIN_LED2
const int PIN_LED3 = 17;    // 定义PIN_LED3
const int PIN_LED4 = 16;    // 定义PIN_LED4
int duration = 0;    // 将持续时间的初始值定义为0
int distance = 0;   // 定义距离的初始值为0
void setup() 
{
  pinMode(TrigPin , OUTPUT); // 设置trigPin为输出模式
  pinMode(EchoPin , INPUT); // 设置echoPin为输入模式
  pinMode(PIN_LED1 , OUTPUT);  // 设置PIN_LED1为输出模式
  pinMode(PIN_LED2 , OUTPUT);  // 设置PIN_LED2为输出模式
  pinMode(PIN_LED3 , OUTPUT);  // 设置PIN_LED3为输出模式
  pinMode(PIN_LED4 , OUTPUT);  // 设置PIN_LED4为输出模式
  Serial.begin(115200);  // 设置波特率为115200.
}
void loop()
{
// 使trigPin高电平输出持续10μs触发HC_SR04 
  digitalWrite(TrigPin , HIGH);
  delayMicroseconds(10);
  digitalWrite(TrigPin , LOW);
// 等待HC-SR04回到高电平，测量这个等待时间
  duration = pulseIn(EchoPin , HIGH);
// 根据时间计算距离
  distance = (duration/2) / 28.5 ;
  Serial.print("Distance: ");
  Serial.print(distance); //串口打印距离值
  Serial.println("cm");
  if ( distance <= 7 )
  {
    digitalWrite(PIN_LED1, HIGH);
  }
  else
  {
    digitalWrite(PIN_LED1, LOW);
  }
  if ( distance <= 14 )
  {
    digitalWrite(PIN_LED2, HIGH);
  }
  else
  {
    digitalWrite(PIN_LED2, LOW);
  }
  if ( distance <= 21 )
  {
    digitalWrite(PIN_LED3, HIGH);
  }
  else
  {
    digitalWrite(PIN_LED3, LOW);
  }
  if ( distance <= 28 )
  {
    digitalWrite(PIN_LED4, HIGH);
  }
  else
  {
    digitalWrite(PIN_LED4, LOW);
  }
} 
```

项目现象：

项目代码上传成功后，利用USB线上电，打开串口监视器，设置波特率为115200。可以看到的现象是：串口监视器窗口将打印超声波传感器和物体之间的距离值，另外，当我们用手在超声波传感器前移动时，相应的LED会亮起来。

### 项目 31：温度仪表

项目介绍：

热敏电阻是一种电阻，其阻值取决于温度和温度的变化，广泛应用于园艺、家庭警报系统等装置中。因此，我们可以利用这一特性来制作温度计。

项目元件：

|  ![](media/8f45d8141f23885af95f870ab64a859c.jpeg)  |  ![](media/2762753d227ba94de9f6e5c9ff79fe53.png)  |  ![](media/3ac518b4caa5086041545c60c7a6a2d1.png)  |
|-|-|-|
| 树莓派Pico板*1 | 树莓派Pico板的扩展板*1 | 公对母杜邦线若干 |
|  ![](media/06b78e3697de1bb564b007ebdcf0581b.png)  |  ![](media/e380dd26e4825be9a768973802a55fe6.png)  |  ![](media/b45bb81bb3763377c63accce606ac5f2.png)  |
| LCD_128X32_DOT*1 | 面包板*1 | 热敏电阻*1 |
|  ![](media/e615c2849ce15bdfc8e26432004124ec.png)  |  ![](media/7dcbd02995be3c142b2f97df7f7c03ce.png)  |  ![](media/b395b1cd2678f87b3a34dec15659efbc.png)  |
| 4P 转杜邦线母单10厘米*1 | USB 线*1 | 10KΩ电阻*1 |

元件知识：

热敏电阻：热敏电阻是一种温度敏感电阻。当热敏电阻感应到温度的变化时，它的电阻就会发生变化。我们可以利用热敏电阻的这种特性来检测温度强度。热敏电阻及其电子符号如下所示。
    
![](media/809b8634747fb295021f12e3b92b7894.png)
    
热敏电阻的电阻值与温度的关系为：
    
![](media/04312ac83502354f253ae6cba9f005dd.wmf)
    
式中：
    
    Rt为热敏电阻在T2温度下的电阻；
    
    R为热敏电阻在T1温度下的标称阻值；
    
    EXP\[n\]是e的n次幂；
    
    B为温度指数；
    
T1，T2是开尔文温度(绝对温度)，开尔文温度=273.15     +摄氏温度。对于热敏电阻的参数，我们使用：B=3950,     R=10KΩ，T1=25℃。热敏电阻的电路连接方法与光敏电阻类似，如下所示：

![](media/ac0d68aac58bffa5c99e1d0ed3a8bc37.jpeg)

我们可以利用ADC转换器测得的值来得到热敏电阻的电阻值，然后利用公式来得到温度值。因此，温度公式可以推导为：

![](media/7d6cc89970b6158b40707eb7b5d3b0d5.wmf)

读取热敏电阻的值：

首先我们学习了热敏电阻读取当前的ADC值、电压值和温度值，并将其打印出来。请按下面的接线图接好线：
    
 ![](media/d1cc8f28e153531547d11ea977c37eca.png)

![](media/934f77b466c3f5391233dbbe62afecd6.png)


```C
/*  
 * 文件名 : 读取热敏电阻模拟值
 * 描述 : 用热敏电阻制作温度计.
 * 作者 : www.keyes-robot.com
*/
#define PIN_ADC1   27
void setup() {
  Serial.begin(115200);
}

void loop() {
  int adcValue = analogRead(PIN_ADC1);                            // 读ADC引脚
  double voltage = (float)adcValue / 1023.0 * 3.3;                // 计算电压
  double Rt = 10 * voltage / (3.3 - voltage);                     //计算热敏电阻的电阻值
  double tempK = 1 / (1 / (273.15 + 25) + log(Rt / 10) / 3950.0); //计算温度(开尔文)
  double tempC = tempK - 273.15;                                  //计算温度(摄氏度)
  Serial.println("Voltage: " + String(voltage) + "V,\t\t" + "Kelvins: " + String(tempK) + "K,\t" + "Temperature: " + String(tempC) + "C");
  delay(1000);
}
```

代码上传到树莓派Pico板成功之后，利用USB线上电，打开串口监视器，设置波特率为115200。你会看到的现象是：串口监视器窗口将不断显示热敏电阻当前的ADC值、电压值和温度值。试着用食指和拇指捏一下热敏电阻(不要碰触导线)一小段时间，你应该会看到温度值增加。

![](media/378f1de2388faafdfb1e7750d78c80d9.png)

温度仪表电路图和接线图：

特别注意：这里必须使用4P     转杜邦线母单10厘米连接LCD_128X32_DOT，LCD_128X32_DOT才会显示正常；否则，使用4P     转杜邦线母单20厘米，LCD_128X32_DOT可能会显示不正常。

![](media/458af2aaa2a441e37e460179529084f7.png)

![](media/954828a7a1afd281d2b8a2a702cf1e83.png)

添加lcd128_32_io库：

前面已经添加过lcd128_32_io库，可以不用重复添加。如果没有添加，就需要添加lcd128_32_io库，添加第三方库的步骤如下:

打开Arduino IDE，单击“项目”→“加载库”→“添加 .ZIP库...”。在弹出窗口中找到该目录下名为：4. Arduino 教程\1. Windows 系统\3.库文件\LCD_128X32.ZIP的文件，先选中LCD_128X32.ZIP文件，再单击“打开”。

![](media/1da6a8c4a21f5553c747e399f77f6239.png)

![](media/0688b70a92fdb6997bba43126f42faee.png)

项目代码：

lcd128_32_io库添加完成后，你可以打开我们提供的代码：

```C
/*  
 * 文件名  : 温度仪表
 * 描述 : LCD 显示热敏电阻的温度.
 * 作者 : www.keyes-robot.com
*/
#include "lcd128_32_io.h"

#define PIN_ADC1  27

lcd lcd(20, 21); //创建 lCD128 *32 引脚，sda->20， scl->21

void setup()  {
  lcd.Init(); //初始化
  lcd.Clear();  //清除
}
char string[10];

void loop() {
  int adcValue = analogRead(PIN_ADC1);                            //读ADC引脚
  double voltage = (float)adcValue / 1023.0 * 3.3;                //计算电压
  double Rt = 10 * voltage / (3.3 - voltage);                     //计算热敏电阻的电阻值
  double tempK = 1 / (1 / (273.15 + 25) + log(Rt / 10) / 3950.0); //计算温度(开尔文)
  double tempC = tempK - 273.15;                                  //计算温度(摄氏度)
  lcd.Cursor(0,0); //设置显示位置
  lcd.Display("Voltage:"); //设置显示参数
  lcd.Cursor(0,8);
  lcd.DisplayNum(voltage);
  lcd.Cursor(0,11);
  lcd.Display("V");
  lcd.Cursor(2,0); 
  lcd.Display("tempC:");
  lcd.Cursor(2,8);
  lcd.DisplayNum(tempC);
  lcd.Cursor(2,11);
  lcd.Display("C");
  delay(200);
}
```

项目现象：

项目代码上传成功后，利用USB线上电，你会看到的现象是：LCD 128X32 DOT的屏幕上显示热敏电阻的电压值和当前环境中的温度值。

### 项目 32：RFID

项目介绍：

现在很多小区的门使用了刷卡开门这个功能，非常的方便。这节课我们将学习使用RFID(射频识别)无线通信技术和对卡（钥匙扣/白卡）进行读、写操作及RFID-MFRC522模块控制舵机转动。

项目元件：

|  ![](media/8eeca2083cc744159c642a792b53eba2.jpeg)  |  ![](media/2762753d227ba94de9f6e5c9ff79fe53.png)  |  ![](media/eb57ae291b76e14c4a3d55966c00f245.png)  |  ![](media/284218a1b5f1d347b1fd3c3119a34695.jpeg)  |
|-|-|-|-|
| 树莓派Pico板*1 | 树莓派Pico板的扩展板*1 | RFID-MFRC522模块*1 | 钥匙扣*1 |
|  ![](media/e615c2849ce15bdfc8e26432004124ec.png)  |  ![](media/cd0bc424e9916881a1a903793821a042.png)  | ![Img](./media/img-20260515092951.png)|  ![](media/7dcbd02995be3c142b2f97df7f7c03ce.png)  |
| 4P 转杜邦线母单20厘米*1 | Servo*1 | 白卡*1 | USB 线*1 |

元件知识：

RFID：RFID (Radio Frequency Identification)是一种无线通信技术。一个完整的RFID系统一般由应答器和读取器组成。通常我们使用标签作为应答器，每个标签都有一个唯一的代码，它附着在物体上，用来识别目标物体。阅读器是用来读取(或写入)标签信息的设备。

从RFID技术衍生的产品可以分为三类：无源RFID产品、有源RFID产品和半有源RFID产品。而无源RFID产品是市场上最早、最成熟、使用最广泛的产品。它在我们的日常生活中随处可见，如公交卡、餐卡、银行卡、酒店门禁卡等，这些都属于近距离接触识别。无源RFID产品的主要工作频率有:125KHZ(低频)、13.56MHZ(高频)、433MHZ(超高频)、915MHZ(超高频)。有源和半有源RFID产品工作在更高的频率。

我们使用的RFID模块是无源RFID产品，工作频率为13.56MHz。

RFID-MFRC522模块：MFRC522是一个高度集成的读取/写入器IC，用于13.56MHz的非接触式通信。MFRC522的内部发射器能够驱动一个读取/写入天线，设计用于与ISO/IEC 14443A
/MIFARE卡和应答器通信，而无需额外的有源电路。接收模块为来自ISO/IEC 14443 A
/MIFARE兼容卡和应答器的信号解调和解码提供了一个健壮和高效的实现。数字模块管理完整的ISO/IEC 14443A组帧和错误检测(奇偶校验和CRC)功能。

该RFID模块采用MFRC522作为控制芯片，采用I2C (Inter－Integrated Circuit)接口。

![](media/eb57ae291b76e14c4a3d55966c00f245.png)

规格参数：

工作电压：DC 3.3V-5V

工作电流：13—100mA/DC 5V

空闲电流：10-13mA/DC 5V

休眠电流：\<80uA

峰值电流：\<100mA

工作频率：13.56MHz

最大功率：0.5W

支持的卡类型：mifare1 S50、mifare1 S70、mifare UltraLight、mifare Pro、mifare Desfire

环境工作温度：摄氏-20—80℃  
环境储存温度：摄氏-40—85℃  
环境相对湿度：相对湿度5%—95%

数据传输速率：最大10Mbit/s

项目接线图：

我们将读取RFID卡的唯一ID号(UID)，识别RFID卡的类型，并通过串口显示相关信息，其接线图如下所示：

![](media/269fa756fbeca7d983e2c42ea027ed48.png)

添加MFRC522_I2C和Wire库：

如果你还没有添加“MFRC522_I2C”和“Wire”库，请在学习之前添加它们。添加第三方库的步骤如下:

打开Arduino IDE，单击“项目”→“加载库”→“添加 .ZIP库...”。在弹出窗口中找到该目录下名为：4. Arduino 教程\1. Windows 系统\3.库文件\MFRC522_I2C.ZIP的文件，先选中MFRC522_I2C.ZIP文件，再单击“打开”。

![](media/51c89d267ce584fd44e12a5999b3f254.png)

![](media/d7a720d392ff806303404efeef158b47.png)

接着在弹出窗口中找到该目录下名为：4. Arduino 教程\1. Windows 系统\3.库文件\Wire.ZIP的文件，先选中Wire.ZIP文件，再单击“打开”。

![](media/afe6f625f14e4719cc994a4aea586020.png)

RFID 读取 UID：

“MFRC522_I2C”和“Wire”库都添加完后，你可以打开我们提供的代码：


```C
/*  
 * 文件名  : RFID
 * 描述 : RFID 读取 UID
 * 作者  : www.keyes-robot.com
*/
#include <Wire.h>
#include "MFRC522_I2C.h"
// IIC引脚默认为Raspberry Pi Pico的GPIO4和GPIO5
// 0x28是SDA的i2c地址，如果不匹配，请检查你的地址与i2c.
MFRC522 mfrc522(0x28);   // 创建 MFRC522.
String rfid_str = "";

void setup() {
  Serial.begin(115200);           // 初始化与PC机的串行通信
  Wire.begin();                   // 初始化 I2C
  mfrc522.PCD_Init();             // 初始化 MFRC522
}

void loop() {
  // 
  if ( ! mfrc522.PICC_IsNewCardPresent() || ! mfrc522.PICC_ReadCardSerial() ) {
    delay(50);
    return;
  }
  
  rfid_str = "";//数字字符串为空
  Serial.print(F("Card UID:"));
  for (byte i = 0; i < mfrc522.uid.size; i++) {
    rfid_str = rfid_str + String(mfrc522.uid.uidByte[i], HEX);  //转化为字符串
    //Serial.print(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " ");
    //Serial.print(mfrc522.uid.uidByte[i], HEX);
  } 
  Serial.println(rfid_str);
}
```

编译并上传代码到树莓派Pico板，代码上传成功后，利用USB线上电，打开串口监视器，设置波特率为115200。你会看到的现象是：将门卡和钥匙扣分别靠近模块感应区，串口监视器窗口将分别显示门卡和钥匙扣的卡号值。如下图所示：

![](media/7af5d38a7288661b9f1143b47ff115b2.png)

特别注意：对于不同的RFID-RC522的门卡和钥匙扣，其门卡值和钥匙扣可能都不一样。

RFID MFRC522的接线图：

现在使用RFID-MFRC522模块、白卡/钥匙扣和舵机模拟做一个智能门禁系统。当白卡/钥匙扣靠近RFID-MFRC522模块感应区舵机转动。按照下图进行接线。
    
 ![](media/da47f135c6abe8c1e49f4803a532a361.png)

添加MFRC522_I2C，Wire和Servo库：

前面已经添加过MFRC522_I2C，Wire和Servo库，可以不用重复添加。如果还没有添加，就需要添加MFRC522_I2C，Wire和Servo库，添加第三方库的步骤如下:

先添加Servo库：

打开Arduino IDE，单击“项目”→“加载库”→“添加 .ZIP库...”。在弹出窗口中找到该目录下名为：4. Arduino 教程\1. Windows 系统\3.库文件\\ Servo.ZIP的文件，先选中Servo.ZIP文件，再单击“打开”。

![](media/20ba44e9a02e804b96e25cfb106b87cc.png)

![](media/78ff6c8cd9179dffc49346d4028b5bcd.png)

再添加MFRC522_I2C和Wire库：

如果在本项目的上面实验（RFID 读取UID）已经添加过MFRC522_I2C和Wire库，就可以不用重复添加；如果还没有添加MFRC522_I2C和Wire库，就参照本项目的上面实验（RFID读取 UID）添加方法添加。

项目代码：

MFRC522_I2C，Wire和Servo库添加完成后，你可以打开我们提供的代码：


```C
/* 
 * 文件名 : RFID mfrc522 控制 舵机
 * 描述 : RFID 控制舵机模拟开门
 * 作者 : www.keyes-robot.com
*/
#include <Servo.h>
#include <Wire.h>
#include <MFRC522_I2C.h>
MFRC522 mfrc522(0x28);
Servo myservo;
String rfid_str = "";

void setup() {
  Serial.begin(115200);
  Wire.begin();
  mfrc522.PCD_Init();
  myservo.attach(2);//舵机连接到GP2
  myservo.write(0);//初始角度是0度
  delay(500);
}

void loop() {
   if ( ! mfrc522.PICC_IsNewCardPresent() || ! mfrc522.PICC_ReadCardSerial() ) {
    delay(50);
    return;
  }
  rfid_str = ""; //设置字符串为空
  Serial.print(F("Card UID:"));
  for (byte i = 0; i < mfrc522.uid.size; i++) {
    rfid_str = rfid_str + String(mfrc522.uid.uidByte[i], HEX);  //转化为字符串
    //Serial.print(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " ");
    //Serial.print(mfrc522.uid.uidByte[i], HEX);
  } 
  Serial.println(rfid_str); 
  if (rfid_str == "93adf720" || rfid_str == "39b646c2") {
    myservo.write(180);            // 舵机旋转180°
    delay(500);
    Serial.println("  open the door!");
    }
}
```

特别注意：对于不同的RFID-MFRC522的白卡和钥匙扣，其RFID-MFRC522读取的白卡和钥匙扣值可能都不一样。你们将自己的RFID-MFRC522模块读取的白卡和钥匙扣值替换程序代码中对应的白卡和钥匙扣值，要不然上传代码至Raspberry Pi Pico上可能会导致白卡和钥匙扣控制不了舵机。

例如:你把程序代码![](media/85170ea6c47d9350dd05efa60be8c808.png)中的rfid_str字符串替换成自己的RFID-MFRC522模块读取的白卡和钥匙扣值。

10. 项目现象：

编译并上传代码到树莓派Pico板，代码上传成功后，利用USB线上电，打开串口监视器，设置波特率为115200；你会看到的现象是：当我们使用白卡或者钥匙卡刷卡时，串口监视器显示出白卡或者钥匙卡信息和“open     the door”，如下图，舵机转动到对应的角度模拟开门。

![](media/4c02154fa98d2481db4b495b18ec2edd.png)

### 项目 33：键盘控制门

项目介绍：

常用的数字按钮传感器，一个按钮就使用一个IO口，而有时我们需要的按钮比较多时，就会占用过多的IO口，为了节省IO口的使用，把多个按钮做成了矩阵类型，通过行列线的控制，实现少IO口控制多个按钮。在本项目中，我们将来学习树莓派Pico板和薄膜4\*4矩阵键盘控制舵机和蜂鸣器。

项目元件：

|  ![](media/7db4bbca6a859c37ae9bfdb52b676bb8.jpeg)  |  ![](media/2762753d227ba94de9f6e5c9ff79fe53.png)  |  ![](media/cd0bc424e9916881a1a903793821a042.png)  |  ![](media/4b4f653a76a82a3b413855493cc58fba.png)  |
|-|-|-|-|
| 树莓派Pico板*1 | 树莓派Pico板的扩展板*1 | 舵机*1 | 有源蜂鸣器*1 |
|  ![](media/fcd187eb009098d691927511606c991b.jpeg)  |  ![](media/e9a8d050105397bb183512fb4ffdd2f6.png)  |  ![](media/e380dd26e4825be9a768973802a55fe6.png)  |  ![](media/3ac518b4caa5086041545c60c7a6a2d1.png)  |
| 薄膜4*4矩阵键盘*1 | 跳线若干 | 面包板*1 | 公对母杜邦线若干 |
|  ![](media/9197d4aff9356c585b7ef68e33a6881d.png)  |  ![](media/098a2730d0b0a2a4b2079e0fc87fd38b.png)  |  ![](media/7dcbd02995be3c142b2f97df7f7c03ce.png)  |  |
| NPN型晶体管(S8050)*1 | 1kΩ电阻*1 | USB 线*1 |  |

元件知识：

4\*4矩阵键盘：键盘是一种集成了许多键的设备。如下图所示，一个4x4键盘集成16个键。
    
![](media/fcd187eb009098d691927511606c991b.jpeg)
    
与LED矩阵集成一样，在4x4键盘中，每排键都是用一根引脚连接，每一列键都是一样的。这样的连接可以减少处理器端口的占用。内部电路如下所示。
    
 ![](media/5ebdacba906622079e0ef41dc1ea3fdf.png)

使用方法类似于矩阵LED，即使用行扫描或列扫描方法检测每列或每行上的键的状态。以列扫描法为例，向第4列(Pin4)发送低电平，检测第1、2、3、4行电平状态，判断A、B、C、D键是否按下。然后依次将低电平发送到列3、2、1，检测是否有其它键被按下。然后，你可以获得所有键的状态。

读取4\*4矩阵键盘的键值：

我们首先使用一个简单的代码读取4\*4矩阵键盘的键值，并将其打印出来，其接线图如下所示：
    
![](media/29629f4ded6b09ca14050716373dcf26.png)

![](media/e7f75540c95c680f6ef9b70704a5cafb.png)


```C
/*  
 * 文件名 : 4x4矩阵键盘显示 
 * 描述 : 获取矩阵键盘的值
 * 作者 : www.keyes-robot.com
*/
#include "Keypad.h"

void setup(){
  Serial.begin(115200);
  keyInit();
}

void loop(){
  char keyValue = getKey(0);
  if (keyValue != '\0')
    Serial.println(keyValue);
  delay(50);
}
```

上传代码成功之后，利用USB线上电，打开串口监视器，设置波特率为115200。可以看到的现象是：按下键盘，串口监视器窗口将打印对应的键值，如下图所示。

![](media/67de90c660afbba9172078400ff14737.png)

键盘控制门的电路图和接线图：

我们使用4\*4矩阵键盘作为键盘来控制舵机和蜂鸣器。

![](media/a3ff2cd6ff82ae35f18f335eda545656.png)

![](media/ff4c106ffcb5041252558ceef89c4006.png)

项目代码：

```C
/*  
 * 文件名 : 键盘控制门
 * 描述 : 制作一个简单的密码锁.
 * 作者 : www.keyes-robot.com
*/
#include "Keypad.h"
#include <Servo.h>

Servo  myservo;     // 创建舵机对象来控制舵机
int servoPin = 2;   // 定义舵机引脚
int buzzerPin = 0; // 定义蜂鸣器引脚

String passWord = "1234"; // 保存正确的密码
String keyIn; 

void setup() {
  keyInit();
  myservo.attach(servoPin);  // 将舵机在servoPin上安装到舵机对象上
  myservo.write(0);                     // 设定舵机的初始位置
  pinMode(buzzerPin, OUTPUT);
  Serial.begin(115200);
  keyIn = "";
  Serial.println(keyIn);
}

void loop() {
  char keyPressed = getKey(0);        // 获取字符输入
  if (keyPressed!='\0') {             // 处理输入字符
    digitalWrite(buzzerPin, HIGH);    // 每次按下该键时发出提示音
    delay(200);
    digitalWrite(buzzerPin, LOW);
    keyIn += keyPressed;              // 保存输入字符
    Serial.println(keyPressed);       // 输入后判断正确性
    if (keyIn.length() == 4) {
      bool isRight = true;            // 保存密码是否正确
      if( passWord != keyIn){
        isRight = !true;
      }
      if (isRight) {                  // 输入的密码是否正确
        myservo.attach(servoPin);
        myservo.write(90);            // 舵机旋转到90°
        delay(2000);                  // 延迟一段时间
        myservo.write(0);             // 舵机旋转到0°
        Serial.println("passWord right!");
      }
      else {                          // 输入密码错误
        digitalWrite(buzzerPin, HIGH);// 输入错误密码提示音
        delay(1000);
        digitalWrite(buzzerPin, LOW);
        Serial.println("passWord error!");
      }
      keyIn = ""; // 将输入字符数重置为0
    }
  }
  delay(200);
}
```

项目现象：

项目代码上传成功后，利用USB线上电，你会看到的现象是：按键盘输入4个字符的密码，如果输入正确（正确密码：1234），舵机会转动一定角度，然后回到原来的位置。如果输入错误，将发出输入错误警报。

![](media/da4ff684cab232f785910863683ff056.png)

### 项目 34：红外遥控控制声音和LED

项目介绍：

红外遥控是一种低成本、易于使用的无线通信技术。IR光与可见光非常相似，除了它的波长稍长一点。这意味着红外线是人眼无法检测到的，这对于无线通信来说是完美的。例如，当你按下电视遥控器上的一个按钮时，一个红外LED会以每秒38000次的频率反复开关，将信息(如音量或频道控制)传送到电视上的红外感光器。

我们将首先解释常见的红外通信协议是如何工作的。然后我们将从一个遥控器和一个红外接收组件开始这个项目。

项目元件：

|  ![](media/17098ffd05750eb6b34eb75b82fbb37a.jpeg)  |  ![](media/2762753d227ba94de9f6e5c9ff79fe53.png)  |  ![](media/88e6b057fb4b0c576c9b2111d15b26e5.png)  |  ![](media/f1a86fc81ab4b043263ce7e01e14d470.png)  |  ![](media/098a2730d0b0a2a4b2079e0fc87fd38b.png)  |
|-|-|-|-|-|
| 树莓派Pico板*1 | 树莓派Pico板的扩展板*1 | 红外接收器 *1 | RGB LED*1 | 220Ω电阻*3 |
|  ![](media/31ab42dc405244fa383c76b57538a6cb.png)  |  ![](media/d1ea1bb2b2749820cab389d5b85b838b.png)  |  ![](media/e9a8d050105397bb183512fb4ffdd2f6.png)  |  ![](media/7dcbd02995be3c142b2f97df7f7c03ce.png)  | |
| 红外遥控器*1 | 无源蜂鸣器*1 |  跳线若干 | USB 线*1 | |
|  ![](media/e380dd26e4825be9a768973802a55fe6.png)  |  ![](media/9197d4aff9356c585b7ef68e33a6881d.png)  |  ![](media/098a2730d0b0a2a4b2079e0fc87fd38b.png)  |  ![](media/3ac518b4caa5086041545c60c7a6a2d1.png)  |  |
| 面包板*1 | NPN型晶体管(S8050)*1 | 1kΩ电阻*1 | 公对母杜邦线若干 |  |

元件知识：

红外(IR)遥控器：是一种具有一定数量按钮的设备。按下不同的按钮会使位于遥控器前端的红外发射管以不同的编码发送红外信号。红外遥控技术应用广泛，如电视、空调等。因此，在当今科技发达社会，红外遥控技术使你切换电视节目和调节空调温度都很方便。

我们使用的遥控器如下所示：

该红外遥控器采用NEC编码，信号周期为110ms。

![](media/28c0a8a6423fa66794502e6db19f997d.jpeg)

红外(IR)接收器：它是一种元件，可以接收红外光，所以可以用它来检测红外遥控器发出的红外光信号。红外接收器解调接收到的红外光信号，并将其转换回二进制，然后将信息传递给微控制器。

红外信号调制过程图：

![](media/b664b5b6884f7d3d8b4ee7eebdfd699b.png)

解码红外信号：

我们按照下面接线图将红外接收元件连接到树莓派Pico板。

![](media/240a9b2efcdd0c0e7099ec5b69beaca6.png)

![](media/5a132241d4bc918d86182d2a4b232ed9.png)

```C
/*  
 * 文件名 : 解码红外信号
 * 描述 : 解码红外遥控器，通过串口打印出来.
 * 作者 : www.keyes-robot.com
*/
#include "IR.h"
#define IR_Pin 16

void setup() {
  Serial.begin(115200);
  IR_Init(IR_Pin);
}

void loop() {
  if(flagCode){
    int irValue = IR_Decode(flagCode);
    Serial.println(irValue, HEX);
    IR_Release();
  }
}
```

代码上传到树莓派Pico板成功之后，利用USB线上电，打开串口监视器，设置波特率为115200。可以看到的现象是：将红外遥控器发射器对准红外接收头，按下红外控制器上的按键，串口监视器窗口将打印当前接收到的按键编码值。

![](media/b928161e22a7c0ee94977850ca706cda.png)

写下红外遥控器与每个按键相关联的按键编码值，因为你稍后将需要这些信息。

![](media/b5c753d6a1f5126fdd8e8423b82a836c.png)

红外遥控的电路图和接线图：

![](media/779caf9e8a0bbb0ecf053fbee11ce3ec.png)

![](media/5a2531a167131af693af10189b4afe36.png)

项目代码：

```C
/*  
 * 文件名 : 红外控制声音和LED
 * 描述 : 红外遥控控制RGB和无源蜂鸣器.
 * 作者 : www.keyes-robot.com
*/
#include "IR.h"

#define irPin 16
#define R_Pin 19
#define G_Pin 18
#define B_Pin 17
#define buzzerPin 15

void setup() {
  Serial.begin(115200);
  IR_Init(irPin);
  pinMode(R_Pin, OUTPUT);
  pinMode(G_Pin, OUTPUT);
  pinMode(B_Pin, OUTPUT);
  pinMode(buzzerPin, OUTPUT);
}

void loop() {
  if(flagCode){
    int irValue = IR_Decode(flagCode);
    Serial.println(irValue, HEX);
    handleControl(irValue);
    IR_Release();
  }
}

void handleControl(unsigned long value) {

  // 处理命令
  if (value == 0xFF6897) // 接收数字“1”
  { 
      analogWrite(R_Pin, 255); //共阴极LED，高电平点亮LED.
      analogWrite(G_Pin, 0);
      analogWrite(B_Pin, 0);
      tone(buzzerPin, 262);//DO音响1秒
      delay(1000);
  }
   else if (value == 0xFF9867) // 接收数字“2”
   { 
      analogWrite(R_Pin, 0); 
      analogWrite(G_Pin, 255); //共阴极LED，高电平点亮LED.
      analogWrite(B_Pin, 0);
      tone(buzzerPin, 294);//Re音响750毫秒
      delay(750);
   }
    else if (value == 0xFFB04F) // 接收数字“3”
   { 
      analogWrite(R_Pin, 0); 
      analogWrite(G_Pin, 0);
      analogWrite(B_Pin, 255); //共阴极LED，高电平点亮LED.
      tone(buzzerPin, 330);//Mi音响625毫秒
      delay(625);
    }
    else if (value == 0xFF30CF) // 接收数字 '4'
   {  
      analogWrite(R_Pin, 255); 
      analogWrite(G_Pin, 255);
      analogWrite(B_Pin, 0);
      tone(buzzerPin, 349);//Fa 音响500毫秒
      delay(500);
    }
    else if (value == 0xFF18E7) // 接收数字 '5'
   {  
      analogWrite(R_Pin, 255); 
      analogWrite(G_Pin, 0);
      analogWrite(B_Pin, 255);
      tone(buzzerPin, 392);//So音响375毫秒
      delay(375);
    }
    else if (value == 0xFF7A85)  // 接收数字 '6'
   {  
      analogWrite(R_Pin, 0); 
      analogWrite(G_Pin, 255);
      analogWrite(B_Pin, 255);
      tone(buzzerPin, 440);//La音响250毫秒
      delay(250);
    }
    else if (value == 0xFF10EF)  // 接收数字 '7'
   {   
      analogWrite(R_Pin, 255); 
      analogWrite(G_Pin, 255);
      analogWrite(B_Pin, 255);
      tone(buzzerPin, 494);//Si音响125毫秒
      delay(125);
    }
    else{
      analogWrite(R_Pin, 0); 
      analogWrite(G_Pin, 0);
      analogWrite(B_Pin, 0);
      noTone(buzzerPin);//Si音响125毫秒
      delay(1000);
      }
  }
```

项目现象：

项目代码上传成功后，利用USB线上电，你会看到的现象是：按红外遥控器的1~7键，可以听到do、re、mi、fa、sol、la、si等蜂鸣器的声音，同时RGB分别亮红灯，绿灯，蓝灯，黄灯，洋红灯，蓝绿灯，白灯。按其他另一按键（除1-7键以外），蜂鸣器就停止播放，RGB熄灭。

(注意:在使用前，我们需要将红外遥控器底部的塑料片去掉)

### 项目 35：WiFi 测试

.实验简介：

ESP8266串口WiFi ESP-01模块，它是一款超低功耗的UART-WiFi     透传模块，拥有业内极富竞争力的封装尺寸和超低能耗技术，专为移动设备和物联网应用设计，可将用户的物理设备连接到Wi-Fi     无线网络上，进行互联网或局域网通信，实现联网功能。

实验元件：

|  ![](media/408f9f823aab6078768f08462eda209b.png)  |  ![](media/37ed4cac4769c9f1c82038709850db57.png)  |
|-|-|
| ESP8266串口WiFi ESP-01*1 | USB转ESP-01S WiFi模块串口测试扩展板*1 |

元件知识：

![](media/37ed4cac4769c9f1c82038709850db57.png)

USB转ESP-01S WiFi模块串口测试扩展板：适用于ESP-01S WiFi模块，扩展板的拨动开关打到flash boot端，直插于电脑USB口，用安信可串口调试助手测试AT指令。

扩展板的拨动开关打到Uart Download端，直插于电脑USB口，ESP-01模块处于下载模式，通过安信可固件下载软件可下载固件到ESP-01模块中。

![](media/408f9f823aab6078768f08462eda209b.png)

ESP8266串口WiFi ESP-01：ESP8266串口WiFi ESP-01是一款超低功耗的UART-WiFi 透传模块，ESP8266串口WiFi ESP-01可广泛应用于智能电网、智能交通、智能家具、手持设备、工业控制等领域。

将WIFI模块串口测试扩展板插入电脑的USB口：

将ESP8266串口WIFI ESP-01模块正确方向插入USB转ESP-01S     WIFI模块串口测试扩展板上。

![](media/91410a61236d00e68e9652b61b4567c3.png)

先将USB转ESP-01S     WIFI模块串口测试扩展板上的拨码开关拨到UartDownload端，再将USB转ESP-01S     WIFI模块串口测试扩展板插入电脑的USB口。

![](media/a325e9270b421dc2c73100ab21c35fc3.jpeg)

安装驱动文件

这个USB转ESP-01S     WIFI模块串口测试扩展板的USB转串口芯片为CH340，我们需要安装这芯片的驱动，驱动为usb_ch341_3.1.2009.06，我们把该驱动文件放到D盘（即：复制![](media/badc756f45490b9720874874dc989f5f.png)放到D盘），然后开始安装驱动。在不同系统在安装驱动方式大同小异，这里我们在win10系统上开始安装驱动。

当USB转ESP-01S WIFI模块串口测试扩展板第一次接入你的电脑,     右击桌面上的“我的电脑”—\>“属性”—\>“设备管理器”,     即可看到“USB-Serial”。

![](media/34560f6c75cb75df7a79d562830c2a16.png)

点击 “USB-Serial”, 选择“更新驱动程序（P）”。

![](media/f7921e767e8a593ebb01a0520f015442.png)

C.然后点击“浏览计算机以查找驱动程序软件”。

![](media/4515dc55a1c27647f9eaab3fd324053d.png)

点击“浏览（R）...”找到提供的““usb_ch341_3.1.2009.06””驱动文件。(我这里是将“usb_ch341_3.1.2009.06”文件放D盘，你也可以把驱动文件夹放在方便的地方也行)

![](media/5494e57dfc38ef78d0c4ed9676bebec5.png)

安装完成后点击“关闭”。

![](media/b77da8913a0b41b268a48ac9e03a4ce6.png)

驱动安装完成后右键点击“我的电脑”—\>“属性”—\>“设备管理器”, 你可以看到你CH340驱动程序已经成功安装到电脑，如下图。

![](media/65160f9c0aad6caaf94db7ce50ac65e6.png)

5.Arduino搭建Esp8266开发环境

先将ESP8266串口WIFI ESP-01模块正确插入USB转ESP-01S WIFI模块串口测试扩展板中，然后将USB转ESP-01S WIFI模块串口测试扩展板插入电脑的USB口，点击进入arduino文件夹（也可以采用最新版本的），找到![](media/4bb50da9c1d2b944fbee752f56ecb966.png)图标并点击进入arduino IDE界面。

![](media/e327d18b359e977eb08a4df0f1652e6a.png)

在Arduino IDE里面进行下载安装：

A.点击“文件”→“首选项”，在“附加开发板管理器网址：”框中复制粘贴这个地址：`http://arduino.esp8266.com/stable/package_esp8266com_index.json`，然后点击“好”保存这个地址。

![](media/2070dd1d46f6190084bba232667e8e0e.png)

先点击“工具”→“开发板：”，再点击“开发板管理器”进入“开发板管理器”页面，IDE会自动下载相关文件，如下图。

![](media/aab6d0692ba09b54abf746660b696243.png)

在“全部”后空格中输入“ESP8266”，然后点击下面搜索内容，选择最新版本进行安装，安装包不大，点击“安装”开始安装相关插件。如下图。（可能会出现下载安装出错，有可能是服务器原因，需要重新点击“安装”就可以了，但由于网络原因，大多用户可能无法搜到esp8266     by esp8266     Community，对于小白而言不推荐使用此方法添加，推荐下面方法2）

![](media/cf82149194b4b17da2f968f9ae835c39.png)

安装成功后点击“关闭”关闭页面，然后点击“工具”→“开发板：”，你可以在里面查看到各种不同型号ESP8266开发板。选择对应的ESP8266开发板型号和COM口，选中后即可对ESP8266进行编程。

![](media/0bad68997d753796bed0c90363c669fd.png)
    
![](media/6206afde171f5d575019401a5cb1cfa6.png)

通过工具对ESP8266进行安装：（推荐使用这种方法）

 A.点击“文件” →“首选项”，在“附加开发板管理器网址：”框中复制粘贴这个地址：`http://arduino.esp8266.com/stable/package_esp8266com_index.json`，然后点击“好”保存这个地址。

![](media/2070dd1d46f6190084bba232667e8e0e.png)

B.使用“esp8266一键安装arduino板_2.5.0版.exe”，一键安装，此方法安装便捷，且安装较快，推荐此方法安装。

![](media/4ddd2bb19d35a38094c45a69b9a68d17.png)

鼠标左键双击“esp8266一键安装arduino板_2.5.0版.exe”，然后就安装完成了。

![](media/26132cf19a193d5ef2396a62de5dab89.png)

在上述工具安装完成之后，重启 Arduino IDE 软件，点击 Arduino菜单栏“工具”→“开发板：”  ，可查看到各种不同型号ESP8266开发板。选择对应的ESP8266开发板型号和COM口，选中后即可对ESP8266进行编程。

![](media/0bad68997d753796bed0c90363c669fd.png)

![](media/6206afde171f5d575019401a5cb1cfa6.png)

实验代码：

注意：打开IDE后，一定要先设置好ESP8266板型和COM口。手机和设备需要连接在同一个WiFi上，如果家里没有WiFi需要打开手机热点共享WiFi，打开手机热点共享WiFi是最好的方法。


```C
/*  
 * 文件名 : Wifi测试
 * 描述 : Wifi模块测试Wifi的ip
 * 作者 : www.keyes-robot.com
*/
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <WiFiClient.h>

#ifndef STASSID
//#define STASSID "your-ssid"
//#define STAPSK  "your-password"
#define STASSID "ChinaNet-2.4G-0DF0"   //用户的wifi名称
#define STAPSK  "ChinaNet@233"       //用户的wifi密码
#endif

const char* ssid = STASSID;
const char* password = STAPSK;

// 端口80的TCP服务器将响应HTTP需求
WiFiServer server(80);

void setup(void) {
  Serial.begin(115200);

  //  连接 WiFi 
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.println("");

  // 等待连接
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  // 设置mDNS响应器::
  // - 在这个例子中。第一个参数是域名
  //   完全限定域名是“esp8266.local”
  // - 第二个参数是IP地址
  //   通过WiFi发送IP地址
  if (!MDNS.begin("esp8266")) {
    Serial.println("Error setting up MDNS responder!");
    while (1) {
      delay(1000);
    }
  }
  Serial.println("mDNS responder started");

  // 激活TCP (HTTP)服务器
  server.begin();
  Serial.println("TCP server started");

  // 将服务器加入MDNS-SD
  MDNS.addService("http", "tcp", 80);
}

void loop(void) {

  MDNS.update();
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
  delay(1000); 
  // 检查客户端是否已连接
  WiFiClient client = server.available();
  if (!client) {
    return;
  }
  Serial.println("");
  Serial.println("New client");

  // 等待来自客户端的有效数据
  while (client.connected() && !client.available()) {
    delay(1);
  }

  // 读取HTTP需求的第一行
  String req = client.readStringUntil('\r');

  // HTTP要求的第一行如下所示:“GET /path HTTP/1.1”
  // 通过查找空格来检索“/path”部分
  int addr_start = req.indexOf(' ');
  int addr_end = req.indexOf(' ', addr_start + 1);
  if (addr_start == -1 || addr_end == -1) {
    Serial.print("Invalid request: ");
    Serial.println(req);
    return;
  }
  req = req.substring(addr_start + 1, addr_end);
  Serial.print("Request: ");
  Serial.println(req);
  client.flush();

  String s;
  if (req == "/") {
    IPAddress ip = WiFi.localIP();
    String ipStr = String(ip[0]) + '.' + String(ip[1]) + '.' + String(ip[2]) + '.' + String(ip[3]);
    s = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n<!DOCTYPE HTML>\r\n<html>Hello from ESP8266 at ";
    s += ipStr;
    s += "</html>\r\n\r\n";
    Serial.println("Sending 200");
  } else {
    s = "HTTP/1.1 404 Not Found\r\n\r\n";
    Serial.println("Sending 404");
  }
  client.print(s);

  Serial.println("Done with client");
}
```

实验现象：

<span style="color: rgb(255, 76, 65);">特别注意：</span> 需要先将项目代码

![](media/18a5a8dea6bf0248eb84ed745fa8d39c.png)

中的用户Wifi名称和用户Wifi密码改成你们自己的Wifi名称和Wifi密码。

Wifi名称和Wifi密码修改后，确保USB转ESP-01S WIFI模块串口测试扩展板上的拨码开关已经拨到Uart Download端，并且也确定USB转ESP-01S WIFI模块串口测试扩展板已经插入电脑的USB口。然后按照前面方法设置ESP8266板型和COM口，IDE右下角会显示对应的ESP8266板型和COM口，再点击![](media/b1feab597beaa43e4293f7cb6d085551.png)将测试代码上传到ESP8266串口WIFI ESP-01模块上，上传成功。（注意：如果上传失败，在板型和COM口没问题下，将USB转ESP-01S WIFI模块串口测试扩展板从电脑的USB口拔下来再次插到电脑的USB口）

![](media/1084c969de37f6cc6b64634302791bf7.png)

WIFI实验代码上传成功后，先将USB转ESP-01S WIFI模块串口测试扩展板从电脑的USB口拔下来，再将USB转ESP-01S WIFI模块串口测试扩展板上的拨码开关拨到Flash Boot端，然后再次插到电脑的USB口上。打开串口监视器，设置波特率为115200，即可看到你的WIFI信息，如下图所示：

![](media/d86715f4fc5bdc64b6b6708bc466d654.png)

### 项目 36：WiFi 智能家居

项目介绍：

在前面的项目35中，我们已经知道ESP8266串口WIFI     ESP-01模块通过WiFi测试代码得到相关的WiFi信息。那么在这个实验中，我们将使用ESP8266串口WiFi     ESP-01模块通过APP和WiFi来控制多个传感器/模块工作，实现WiFi智能家居的效果。

项目元件：

|  ![](media/b95461f3200b72ebb02ee422c2f6a91c.jpeg)  |  ![](media/2762753d227ba94de9f6e5c9ff79fe53.png)  |  ![](media/ad6564bfdbf1c8a5c41da748f5529039.png)  |
|-|-|-|
| 树莓派Pico板*1 | 树莓派Pico板的扩展板*1 | 温湿度传感器*1 |
|  ![](media/408f9f823aab6078768f08462eda209b.png)  |  ![](media/37ed4cac4769c9f1c82038709850db57.png)  |  ![](media/3a3ee73278937b4a7560cf124aacde36.png)  |
| ESP8266串口WIFI ESP-01*1 | USB转ESP-01S WIFI模块串口测试扩展板*1 | 130直流电机模块*1 |
|  ![](media/c141037e768eebd697ea07520708ee47.png)  |  ![](media/41abce34fdbca029fdea842bba8208c0.png) ![](media/989d26695fd03ea630b7fdf186ff78c1.png)  |  ![](media/11163a0fe0cd3efaf4ccd57fb237f103.png)  |
| 5V继电器模块*1 | 智能手机/平板电脑*1 | 超声波传感器*1 |
|  ![](media/1886ee7e1faeea2c093ae626e1b8baaf.png)  |  ![](media/df84a18afd8b37097c469ae3ac208bc4.png)  |  ![](media/7dcbd02995be3c142b2f97df7f7c03ce.png)  |
| 舵机*1 | 母对母杜邦线若干 | USB 线*1 |
|  ![](media/e380dd26e4825be9a768973802a55fe6.png)  |  ![](media/e615c2849ce15bdfc8e26432004124ec.png)  |  ![](media/ebfacf0e05bcd3941fe3b641935756b0.png)  |
| 面包板*1 | 4P 转杜邦线母单20厘米*2 | 3Pin 杜邦母单*2 |
|  ![](media/b006569d007392f7ebcd1a2d84c0c942.png)  |  ![](media/824d7ee32f701ea11eb91a9d1865d6ac.png)  |  |
| 公对母杜邦线若干 | 三叶软桨*1 |  |

将WIFI模块串口测试扩展板插入电脑的USB口：

将ESP8266串口WIFI ESP-01模块正确方向插入USB转ESP-01S WIFI模块串口测试扩展板上。

![](media/91410a61236d00e68e9652b61b4567c3.png)

先将USB转ESP-01S WIFI模块串口测试扩展板上的拨码开关拨到UartDownload端，再将USB转ESP-01S WIFI模块串口测试扩展板插入电脑的USB口。

![](media/2a5c6aa3eae6044febd72ff13c0b948d.jpeg)

ESP8266 代码：

注意：打开Arduino IDE后，一定要先设置好ESP8266板型和COM口。手机和设备需要连接在同一个WiFi上，如果家里没有WiFi需要打开手机热点共享WiFi，打开手机热点共享WiFi是最好的方法。

```C
/*
ESP8266_Code
*/
// generated by KidsBlock
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <WiFiClient.h>
//#include <WiFi.h>

#ifndef STASSID
#define STASSID "ChinaNet-2.4G-0DF0"  //用户的Wifi名称
#define STAPSK  "ChinaNet@233"       //用户的wifi密码
#endif
const char* ssid = STASSID;
const char* password = STAPSK;

//IPAddress local_IP(192,168,4,22);
//IPAddress gateway(192,168,4,22);
//IPAddress subnet(255,255,255,0);
//
//const char *ssid = "ESP8266_AP_TEST";
//const char *password = "12345678";

WiFiServer server(80);
String unoData = "";
int ip_flag = 0;
int ultra_state = 1;
String ip_str;


void setup() {
  Serial.begin(9600); 
//   WiFi.mode(WIFI_AP); //设置为AP模式
//
//  WiFi.softAPConfig(local_IP, gateway, subnet); //设置AP地址
//  while(!WiFi.softAP(ssid, password)){}; //开始 AP
//  Serial.println("AP starting success");
//
//  Serial.print("IP address: ");
//  Serial.println(WiFi.softAPIP()); // 打印IP地址
//
//  WiFi.softAPsetHostname("myHostName"); //设置主机名
//  Serial.print("HostName: ");
//  Serial.println(WiFi.softAPgetHostname()); //打印主机名
//
//  Serial.print("mac Address: ");
//  Serial.println(WiFi.softAPmacAddress()); //打印mac地址

  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.print("IP ADDRESS: ");
  Serial.println(WiFi.localIP());
  if (!MDNS.begin("esp8266")) {
    //Serial.println("Error setting up MDNS responder!");
    while (1) {
      delay(1000);
    }
  }
 // Serial.println("mDNS responder started");
  server.begin();
  //Serial.println("TCP server started");
  MDNS.addService("http", "tcp", 80);
  ip_flag = 1;
}

void loop() {
  //Serial.println(WiFi.softAPgetStationNum()); //打印客户端连接数
  if(ip_flag == 1)
  {
    for(int i=3; i>0; i--)
    {
      Serial.print("IP: ");
      Serial.print(WiFi.localIP());
      Serial.println('#');
      delay(500);
    }
    ip_flag = 0;
    
  }
    MDNS.update();
    WiFiClient client = server.available();
    if (!client) {
      return;
    }
    //Serial.println("");
    while (client.connected() && !client.available()) {
      delay(1);
    }
    String req = client.readStringUntil('\r');
    int addr_start = req.indexOf(' ');
    int addr_end = req.indexOf(' ', addr_start + 1);
    if (addr_start == -1 || addr_end == -1) {
      //Serial.print("Invalid request: ");
      //Serial.println(req);
      return;
    }
    req = req.substring(addr_start + 1, addr_end);
    int len_val = String(req).length();
    String M_req = String(req).substring(0,6);
    //Serial.println(M_req);
    if(M_req == "/")
    {
      String s_M_req = String(req).substring(5,len_val);
      Serial.print(s_M_req);
      Serial.print("#");
    }
    if(M_req == "/btn/v")
    {
      String s_M_req = String(req).substring(5,len_val);
      Serial.print(s_M_req);
      Serial.print("#");
    }
    client.flush();
    String s;
    if (req == "/") {
      IPAddress ip = WiFi.localIP();
      String ipStr = String(ip[0]) + '.' + String(ip[1]) + '.' + String(ip[2]) + '.' + String(ip[3]);
      s = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n<!DOCTYPE HTML>\r\n<html>Hello from ESP8266 at ";
      s += ipStr;
      s += "</html>\r\n\r\n";
      //Serial.println("Sending 200");
      Serial.println(WiFi.localIP());
      Serial.write('*');
      client.println(WiFi.localIP());
      ip_flag = 0;
    }
    else if(req == "/btn/0")
    {
      Serial.write('a');
      client.println("turn on the relay");
    }
    else if(req == "/btn/1")
    {
      Serial.write('b');
      client.println("turn off the relay");
    }
    else if(req == "/btn/2")
    {
      Serial.write('c');
      client.println("Bring the steering gear over 180 degrees");
    }
    else if(req == "/btn/3")
    {
      Serial.write('d');
      client.println("Bring the steering gear over 0 degrees");
    }
    else if(req == "/btn/4")
    {
      Serial.write('e');
      client.println("esp8266 already turn on the fans");
    }
    else if(req == "/btn/5")
    {
      Serial.write('f');
      client.println("esp8266 already turn off the fans");
    }
    else if(req == "/btn/6")
    {
      Serial.write('g');
      while(Serial.available() > 0)
      {
        unoData = Serial.readStringUntil('#');
        client.println(unoData);
      }
    }
    else if(req == "/btn/7")
    {
      Serial.write('h');
      client.println("turn off the ultrasonic");
    }
    else if(req == "/btn/8")
    {
      Serial.write('i');
      while(Serial.available() > 0)
      {
        unoData = Serial.readStringUntil('#');
        client.println(unoData);
        //client.flush();
      }
    }
    else if(req == "/btn/9")
    {
      Serial.write('j');
      client.println("turn off the temperature");
    }
    else if(req == "/btn/10")
    {
      Serial.write('k');
      while(Serial.available() > 0)
      {
        unoData = Serial.readStringUntil('#');
        client.println(unoData);
        //client.flush();
      }
    }
    else if(req == "/btn/11")
    {
      Serial.write('l');
      client.println("turn off the humidity");
    }
    else if(req == "/btn/12")
    {
      Serial.write('m');
      client.println(F("m"));
    }
    else if(req == "/btn/13")
    {
      Serial.write('n');
      client.println(F("n"));
    }
    else if(req == "/btn/14")
    {
      Serial.write('o');
      client.println(F("o"));
    }
    else if(req == "/btn/15")
    {
      Serial.write('p');
      client.println(F("p"));
    }
    else if(req == "/btn/16")
    {
      Serial.write('q');
      client.println(F("q"));
    }
    else if(req == "/btn/17")
    {
      Serial.write('r');
      client.println(F("r"));
    }
    else if(req == "/btn/18")
    {
      Serial.write('s');
      client.println(F("s"));
    }
    else if(req == "/btn/19")
    {
      Serial.write('t');
      client.println(F("t"));
    }
    else if(req == "/btn/20")
    {
      Serial.write('u');
      client.println(F("u"));
    }
    else if(req == "/btn/21")
    {
      Serial.write('v');
      client.println(F("v"));
    }
    else if(req == "/btn/22")
    {
      Serial.write('w');
      client.println(F("w"));
    }
    else if(req == "/btn/23")
    {
      Serial.write('x');
      client.println(F("x"));
    }
    else {
      //s = "HTTP/1.1 404 Not Found\r\n\r\n";
      //Serial.println("Sending 404");
    }

    client.print(F("IP : "));
    client.println(WiFi.localIP());
}

```

特别注意：需要先将项目代码

![](media/18a5a8dea6bf0248eb84ed745fa8d39c.png)

中的用户Wifi名称和用户Wifi密码改成你们自己的Wifi名称和Wifi密码。

Wifi名称和Wifi密码修改后，确保USB转ESP-01S WiFi模块串口测试扩展板上的拨码开关已经拨到Uart Download端，并且也确定USB转ESP-01S WIFI模块串口测试扩展板已经插入电脑的USB口。然后按照项目35中的方法设置ESP8266板型和COM口，IDE右下角会显示对应的ESP8266板型和COM口，再点击![](media/b1feab597beaa43e4293f7cb6d085551.png)将ESP8266
代码上传到ESP8266串口WIFI ESP-01模块上，上传成功。（注意：如果上传失败，在板型和COM口没问题情况下，将USB转ESP-01S WIFI模块串口测试扩展板从电脑的USB口拔下来再次插到电脑的USB口）

![](media/2acfe216634719a78a57a09224f487fa.png)

ESP8266 代码上传成功后，先将USB转ESP-01S WiFi模块串口测试扩展板从电脑的USB口拔下来，再将ESP8266串口WIFI ESP-01模块从USB转ESP-01S WiFi模块串口测试扩展板上拔下来。

项目接线：

| 继电器 | 树莓派Pico板的扩展板 |  | 温湿度传感器 | 树莓派Pico板的扩展板 |
|-|-|-|-|-|
| G | G | G | G |
| V | 5V | V | 3V3 |
| S | GP27 | S | GP2(S) |
| 超声波传感器 | 树莓派Pico板的扩展板 |  | 130 风扇模块 | 树莓派Pico板的扩展板 |
| Vcc | 5V | G | G |
| Trig | GP17 | V | 5V |
| Echo | GP16 | IN+ | GP3 |
| Gnd | G | IN- | GP5 |
| Wifi 模块 | 树莓派Pico板的扩展板 |  | 舵机 | 树莓派Pico板的扩展板 |
| 3V3 | 3V3 | Red line | 3V3 |
| EN/CP | 3V3 | Brown line | G |
| TX | RX(GP1) | Orange line | GP9(S) |
| RX | TX(GP0) |  |  |
| GND | GND |  |  |  |

![](media/27fce3ad3262cd05fc46fcb4bc0aa7c3.png)

WiFi Smart Home项目代码：

注意：打开Arduino IDE后，一定要先设置好树莓派Pico板的板型和COM口。如果家里没有WiFi需要打开手机热点共享WiFi.


```C
/*  
 * 文件名 : WiFi 智能家居.
 * 描述 : WiFi APP控制多个传感器/模块工作，实现WiFi智能家居效果.
 * 作者 : www.keyes-robot.com
*/
#include <dht.h>
dht DHT;

#include<Servo.h>
Servo myservo;

char wifiData;
int distance1;
String dis_str;

const int dhtPin = 2;
const int relayPin = 27;
const int IN1 = 3;
const int IN2 = 5;
const int trigPin = 17;
const int echoPin = 16;
const int servoPin = 9;

int ip_flag = 1;
int ultra_state = 1;
int temp_state = 1;
int humidity_state = 1;

void setup() {
  Serial1.begin(9600);
  pinMode(dhtPin, INPUT);
  pinMode(relayPin, OUTPUT);
  pinMode(servoPin, OUTPUT);
  pinMode(IN1, OUTPUT);
  pinMode(IN2, OUTPUT);
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);

  //turn off the fan
  digitalWrite(IN1, LOW);
  digitalWrite(IN2, LOW);

  digitalWrite(relayPin, LOW); //关闭继电器

  myservo.attach(9);

  //dht.begin();
}

void loop() {
  int chk = DHT.read11(dhtPin);
  if(Serial1.available() > 0)
  {
    wifiData = Serial1.read();
    Serial.print(wifiData);
    if(wifiData == '#')
    {
      ip_flag = 0;
    }
    
    if(ip_flag == 1)
    {
      //String ip_addr = Serial.readStringUntil('#');
      Serial.print(wifiData);
      if(wifiData == '#')
      {
        Serial.println("");
      }
      delay(100);
    }
  }

  switch(wifiData)
    {
      case 'a': digitalWrite(relayPin, HIGH); break;
      case 'b': digitalWrite(relayPin, LOW); break;
      case 'c': myservo.write(180); delay(200); break;
      case 'd': myservo.write(0); delay(200); break;
      case 'e': digitalWrite(IN1, HIGH); digitalWrite(IN2, LOW); break;
      case 'f': digitalWrite(IN1, LOW); digitalWrite(IN2, LOW); break;
      case 'g': while(ultra_state>0)
                  {
                    Serial.print("Distance = "); 
                    Serial.print(checkdistance());
                    Serial.println("#"); 
                    Serial1.print("Distance = "); 
                    Serial1.print(checkdistance());
                    Serial1.println("#"); 
                    ultra_state = 0;
                  }
                  break;
      case 'h': ultra_state = 1; break;
      case 'i': while(temp_state>0)
                {
                  Serial.print("Temperature = "); 
                  Serial.print(DHT.temperature,1);
                  Serial.println("#");
                  Serial1.print("Temperature = "); 
                  Serial1.print(DHT.temperature,1);
                  Serial1.println("#");
                  temp_state = 0;
                }
                break;
      case 'j': temp_state = 1; break;
      case 'k': while(humidity_state > 0)
                {
                  Serial.print("Humidity = "); 
                  Serial.print(DHT.humidity,1);
                  Serial.println("#");
                  Serial1.print("Humidity = "); 
                  Serial1.print(DHT.humidity,1);
                  Serial1.println("#");
                  humidity_state = 0;
                }
                break;
      case 'l': humidity_state = 1; break;
    }
  
}

int checkdistance() {
  digitalWrite(17, LOW);
  delayMicroseconds(2);
  digitalWrite(17, HIGH);
  delayMicroseconds(10);
  digitalWrite(17, LOW);
  int distance = pulseIn(16, HIGH) / 58;
  
  delay(10);
  return distance;
}
```

实现现象：

特别注意：上传项目代码前，需要先将连接到树莓派Pico板的扩展板上的TX和RX的杜邦线先拔下来，要不然代码上传可能不成功。

然后点击Arduino IDE菜单栏的"工具" →"开发板："，选择“Raspberry Pi Pico”，选择正确的COM端口，最后将WiFi智能家居项目代码上传至树莓派Pico板。上传代码成功后，再将接在ESP8266串口WIFI ESP-01模块上的TX杜邦线另一端接到树莓派Pico板的扩展板上的RX(GP1)引脚，RX的杜邦线另一端接到树莓派Pico板的扩展板上的TX(GP0)引脚。点击![](media/c26260f4b82d19ca26aeafe9722c59ee.png)打开串口监视器窗口，将波特率设置为9600。这样，串口监视器就显示此时你们WiFi的IP地址。（WiFi的IP地址有时候会改变，如果原来的IP地址不行，需要重新检测WiFi的IP地址）

![](media/62c973921a23e69e9a7f93ff30887d86.png)

![](media/5b591e3f33f59feeebb6ac4eee82763c.png)

![](media/fc090c9a718831aaf6a20421d8c9796e.png)

**APP下载安装**

⚠️ **<span style="color: rgb(255, 76, 65);">特别提醒：</span>如果前面已经下载安装了APP，则这一步骤可以直接跳过。**

**步骤1：** 在手机/平板浏览器的搜索框中输入官网链接： www.keyes-robot.com

![](media/wyx1.png)

**步骤2：** 找到 “**资料中心**”，并且点击它。

![](media/wyx2.png)

**步骤3：** 在页面找到 “**APP下载**”选项，并且点击它。

![](media/wyx3.png)

**步骤4：** 在 “**APP下载**” 页面，找到 “**keyes wifi**”。

![](media/wyx4.png)

**步骤5：** 根据自己的手机/平板系统选择对应的APP下载安装。选择如下：

![](media/wyx7.png)

**安卓系统**

a\. 点击 "**点击下载**" 按钮，下载对应的 "**keyes wifi.apk**" 文件。

![](media/wyx5.png)

b\. 按照安装提示进行安装。

![](media/d620452a9d6188cb3946269510df5ae0.png)

![](media/37a6343d672017cc55da02436b8a82a2.png)

![](media/b2c5bddc60b9c017747a56eea24e412a.png)

![](media/27708677feed5d3acfb062d9222bacda.png)

c\. 下载安装后，单击打开，出现如下图界面。

![](media/c49919d6ee38ca619aaf90aa9b94d4fc.png)


**苹果系统**

a\. 点击 "**跳转APP Store**" 按钮，跳转到 APP Store ![](media/APP-Store.png)


![](media/wyx6.png)


b\. 在 APP Store 上搜索 **keyes wifi** ，选择 **keyes wifi** ，然后点击 “**获取**”，下载安装APP即可。

![](media/WASQ11.png)

c\. 下载安装后, 单击“**打开**”，出现如下图界面。

![Img](./media/img-20251211100456.png)


<span style="color: rgb(255, 76, 0);">**注意：点击APP上的按钮，ESP8266串口WIFI ESP-01模块上的蓝色指示灯会闪烁，出现指示灯闪烁最亮的时候说明APP已经连接上WIFI。**</span>

APP已经连接上了WIFI后，开始进行如下操作：

点击![](media/5b9754cb6ec4f995c9eada1da89a8969.png)按钮，继电器打开，APP上显示![](media/505b00b0e23f6498c5d51d5d775c8fcb.png)，模块上的指示灯点亮；再次点击![](media/5b9754cb6ec4f995c9eada1da89a8969.png)按钮，继电器关闭，APP上显示![](media/deb54a77cdcc87d7569e8b8e46de129f.png)，模块上的指示灯不亮。

点击![](media/c54f78d819d4e6a8310eaeb79ff66910.png)按钮，舵机转动180°，APP上显示![](media/d6feecf1992cbaf09f03c20b5b7f5414.png)；再次点击![](media/c54f78d819d4e6a8310eaeb79ff66910.png)按钮，APP上显示![](media/dee12bee3866542bfe5d70a539f79f0b.png)，舵机转动0°。

点击![](media/5490abf5b2f8a1d9cea3055da07c251c.png)按钮，电机（带小风扇叶）转动，APP上显示![](media/e609128934bc72a89b39ded5833d050e.png)；再次点击![](media/5490abf5b2f8a1d9cea3055da07c251c.png)按钮，关闭电机，APP上显示![](media/4f450510172cdd7c4d8a7c6b39881a90.png)；

点击![](media/95bfbe879d2391e4e48dcae085abe5a6.png)按钮，超声波传感器测距，在超声波传感器前放一个物体，APP上显示![](media/676c8a750e95c84272b0b7791f7b3cd3.png)（不同的距离显示不同的数字），说明此时物体离超声波传感器的距离为14cm；再次点击![](media/95bfbe879d2391e4e48dcae085abe5a6.png)按钮，关闭超声波，APP上显示![](media/b1df35af68601022e54b7e575b0a07c7.png)。

点击![](media/08c8a35841b31fa4b5327fb7b23a7af5.png)按钮，温湿度传感器测量环境中的温度，APP上显示![](media/2a40f3e895808a3c6d4e1f542133feba.png)，说明此时环境中的温度为28℃；再次点击![](media/08c8a35841b31fa4b5327fb7b23a7af5.png)按钮，关闭温湿度传感器，APP上显示![](media/82887a1385bc7411ecbdc41f60ebd450.png)。

点击![](media/d8e3463ab2f644b3300cdeaa2a68e4c2.png)按钮，温湿度传感器测量环境中的湿度，APP上显示![](media/cc825e69cf073aa73e37712330f4726e.png)，说明此时环境中的湿度为52%；再次点击![](media/d8e3463ab2f644b3300cdeaa2a68e4c2.png)按钮，关闭温湿度传感器，APP上显示![](media/adc18d06e626af067286da9040c20252.png)。
